#define SPRAWL_LOG_LEVEL_TYPE LogLevel #define SPRAWL_MINIMUM_LOG_LEVEL DEBUG #include "../logging/Logging.hpp" #include "../filesystem/filesystem.hpp" #include "../filesystem/path.hpp" #include "../threading/thread.hpp" #include "gtest_helpers.hpp" #include <gtest/gtest.h> #ifdef _WIN32 #include <Windows.h> #endif enum class LogLevel { TRACE, DEBUG, INFO }; TEST(LoggingTest, BasicLoggingToFileWorks) { sprawl::logging::SetRenameMethod(sprawl::logging::RenameMethod::Counter, 5); sprawl::logging::SetDefaultHandler(sprawl::logging::PrintToFile("test.log")); sprawl::logging::Init(); int line = __LINE__; LOG(INFO, sprawl::logging::Category("Test", "Test"), "This is my message."); sprawl::logging::ShutDown(); ASSERT_TRUE(sprawl::path::Exists("test.log")); sprawl::filesystem::File f = sprawl::filesystem::Open("test.log", "r"); //Skip timestamp. f.Seek(27, sprawl::filesystem::RelativeTo::Beginning); EXPECT_EQ( sprawl::Format("{} [INFO] This is my message. [Test::Test] (", sprawl::this_thread::GetHandle().GetUniqueId()) + sprawl::path::Basename(__FILE__) + sprawl::String(":") + sprawl::String(__FUNCTION__) + sprawl::String(":{}){}").format(line + 1, sprawl::filesystem::LineSeparator()), f.Read() ); f.Close(); EXPECT_TRUE(sprawl::filesystem::Remove("test.log")); } TEST(LoggingTest, ThreadedLoggingToFileWorks) { sprawl::logging::SetRenameMethod(sprawl::logging::RenameMethod::Counter, 5); sprawl::logging::SetDefaultHandler(sprawl::logging::RunHandler_Threaded(sprawl::logging::PrintToFile("test.log"))); sprawl::logging::Init(); int line = __LINE__; LOG(INFO, sprawl::logging::Category("Test", "Test"), "This is my message."); sprawl::logging::ShutDown(); ASSERT_TRUE(sprawl::path::Exists("test.log")); sprawl::filesystem::File f = sprawl::filesystem::Open("test.log", "r"); //Skip timestamp. f.Seek(27, sprawl::filesystem::RelativeTo::Beginning); EXPECT_EQ( sprawl::Format("{} [INFO] This is my message. [Test::Test] (", sprawl::this_thread::GetHandle().GetUniqueId()) + sprawl::path::Basename(__FILE__) + sprawl::String(":") + sprawl::String(__FUNCTION__) + sprawl::String(":{}){}").format(line + 1, sprawl::filesystem::LineSeparator()), f.Read() ); f.Close(); EXPECT_TRUE(sprawl::filesystem::Remove("test.log")); } TEST(LoggingTest, FormattingWorks) { sprawl::logging::SetRenameMethod(sprawl::logging::RenameMethod::Counter, 5); sprawl::logging::SetDefaultHandler(sprawl::logging::PrintToFile("test.log")); sprawl::logging::SetFormat("{category} -- {level} -- {message} | {{{function} - {file}:{line}} {blah}"); sprawl::logging::Init(); int line = __LINE__; LOG(INFO, sprawl::logging::Category("Test", "Test"), "This is my message."); sprawl::logging::ShutDown(); ASSERT_TRUE(sprawl::path::Exists("test.log")); sprawl::filesystem::File f = sprawl::filesystem::Open("test.log", "r"); EXPECT_EQ( sprawl::String("Test::Test -- INFO -- This is my message. | {") + sprawl::String(__FUNCTION__) + sprawl::String(" - ") + sprawl::path::Basename(__FILE__) + sprawl::String(":{}} {blah}{}").format(line + 1, sprawl::filesystem::LineSeparator()), f.Read() ); f.Close(); EXPECT_TRUE(sprawl::filesystem::Remove("test.log")); } TEST(LoggingTest, PrintToStdoutWorks) { #ifdef _WIN32 int oldStdoutid = _dup(1); FILE* newStdout = fopen("test.log", "w"); _dup2(_fileno(newStdout), 1); #else FILE* oldStdout = stdout; stdout = fopen("test.log", "w"); #endif sprawl::logging::SetRenameMethod(sprawl::logging::RenameMethod::Counter, 5); sprawl::logging::SetDefaultHandler(sprawl::logging::PrintToStdout()); sprawl::logging::Init(); int line = __LINE__; LOG(INFO, sprawl::logging::Category("Test", "Test"), "This is my message."); sprawl::logging::ShutDown(); #ifdef _WIN32 fflush(stdout); fclose(newStdout); _dup2(oldStdoutid, 1); _close(oldStdoutid); #else fclose(stdout); stdout = oldStdout; #endif ASSERT_TRUE(sprawl::path::Exists("test.log")); sprawl::filesystem::File f = sprawl::filesystem::Open("test.log", "r"); //Skip timestamp. f.Seek(27, sprawl::filesystem::RelativeTo::Beginning); EXPECT_EQ( sprawl::Format("{} [INFO] This is my message. [Test::Test] (", sprawl::this_thread::GetHandle().GetUniqueId()) + sprawl::path::Basename(__FILE__) + sprawl::String(":") + sprawl::String(__FUNCTION__) + sprawl::String(":{}){}").format(line + 1, sprawl::filesystem::LineSeparator()), f.Read() ); f.Close(); EXPECT_TRUE(sprawl::filesystem::Remove("test.log")); } TEST(LoggingTest, PrintToStderrWorks) { #ifdef _WIN32 int oldStderrid = _dup(2); FILE* newStderr = fopen("test.log", "w"); _dup2(_fileno(newStderr), 2); #else FILE* oldStderr = stderr; stderr = fopen("test.log", "w"); #endif sprawl::logging::SetRenameMethod(sprawl::logging::RenameMethod::Counter, 5); sprawl::logging::SetDefaultHandler(sprawl::logging::PrintToStderr()); sprawl::logging::Init(); int line = __LINE__; LOG(INFO, sprawl::logging::Category("Test", "Test"), "This is my message."); sprawl::logging::ShutDown(); #ifdef _WIN32 fflush(stderr); fclose(newStderr); _dup2(oldStderrid, 2); _close(oldStderrid); #else fclose(stderr); stderr = oldStderr; #endif ASSERT_TRUE(sprawl::path::Exists("test.log")); sprawl::filesystem::File f = sprawl::filesystem::Open("test.log", "r"); //Skip timestamp. f.Seek(27, sprawl::filesystem::RelativeTo::Beginning); EXPECT_EQ( sprawl::Format("{} [INFO] This is my message. [Test::Test] (", sprawl::this_thread::GetHandle().GetUniqueId()) + sprawl::path::Basename(__FILE__) + sprawl::String(":") + sprawl::String(__FUNCTION__) + sprawl::String(":{}){}").format(line + 1, sprawl::filesystem::LineSeparator()), f.Read() ); f.Close(); EXPECT_TRUE(sprawl::filesystem::Remove("test.log")); } TEST(LoggingTest, CategoryHandlersWork) { sprawl::logging::SetRenameMethod(sprawl::logging::RenameMethod::Counter, 5); sprawl::logging::SetDefaultHandler(sprawl::logging::PrintToFile("test.log")); sprawl::logging::AddCategoryHandler(sprawl::logging::Category("Test"), sprawl::logging::PrintToFile("test2.log")); sprawl::logging::AddCategoryHandler(sprawl::logging::Category("Test", "Test"), sprawl::logging::PrintToFile("test3.log"), sprawl::logging::CategoryCombinationType::Exclusive); sprawl::logging::AddCategoryHandler(sprawl::logging::Category("Test", "Test3"), sprawl::logging::PrintToFile("test4.log")); sprawl::logging::AddCategoryHandler(sprawl::logging::Category("Test", "Test3", "Test4"), sprawl::logging::PrintToFile("test5.log")); sprawl::logging::Init(); int line = __LINE__; LOG(INFO, sprawl::logging::Category("Test", "Test"), "This is my message."); int line2 = __LINE__; LOG(INFO, sprawl::logging::Category("Test", "Test2"), "This is my message."); int line3 = __LINE__; LOG(INFO, sprawl::logging::Category("Test"), "This is my message."); int line4 = __LINE__; LOG(INFO, sprawl::logging::Category("NotTest"), "This is my message."); int line5 = __LINE__; LOG(INFO, sprawl::logging::Category("Test", "Test3"), "This is my message."); int line6 = __LINE__; LOG(INFO, sprawl::logging::Category("Test", "Test3", "Test4"), "This is my message."); sprawl::logging::ShutDown(); ASSERT_TRUE(sprawl::path::Exists("test.log")); ASSERT_TRUE(sprawl::path::Exists("test2.log")); ASSERT_TRUE(sprawl::path::Exists("test3.log")); ASSERT_TRUE(sprawl::path::Exists("test4.log")); sprawl::filesystem::File f = sprawl::filesystem::Open("test.log", "r"); //Skip timestamp. f.Seek(27, sprawl::filesystem::RelativeTo::Beginning); EXPECT_EQ( sprawl::Format("{} [INFO] This is my message. [NotTest] (", sprawl::this_thread::GetHandle().GetUniqueId()) + sprawl::path::Basename(__FILE__) + sprawl::String(":") + sprawl::String(__FUNCTION__) + sprawl::String(":{}){}").format(line4 + 1, sprawl::filesystem::LineSeparator()), f.Read() ); f.Close(); f = sprawl::filesystem::Open("test2.log", "r"); //Skip timestamp. f.Seek(27, sprawl::filesystem::RelativeTo::Beginning); EXPECT_EQ( sprawl::Format("{} [INFO] This is my message. [Test::Test2] (", sprawl::this_thread::GetHandle().GetUniqueId()) + sprawl::path::Basename(__FILE__) + sprawl::String(":") + sprawl::String(__FUNCTION__) + sprawl::String(":{}){}").format(line2 + 1, sprawl::filesystem::LineSeparator()), f.ReadLine() ); //Skip timestamp. f.Seek(27, sprawl::filesystem::RelativeTo::CurrentPosition); EXPECT_EQ( sprawl::Format("{} [INFO] This is my message. [Test] (", sprawl::this_thread::GetHandle().GetUniqueId()) + sprawl::path::Basename(__FILE__) + sprawl::String(":") + sprawl::String(__FUNCTION__) + sprawl::String(":{}){}").format(line3 + 1, sprawl::filesystem::LineSeparator()), f.ReadLine() ); //Skip timestamp. f.Seek(27, sprawl::filesystem::RelativeTo::CurrentPosition); EXPECT_EQ( sprawl::Format("{} [INFO] This is my message. [Test::Test3] (", sprawl::this_thread::GetHandle().GetUniqueId()) + sprawl::path::Basename(__FILE__) + sprawl::String(":") + sprawl::String(__FUNCTION__) + sprawl::String(":{}){}").format(line5 + 1, sprawl::filesystem::LineSeparator()), f.ReadLine() ); //Skip timestamp. f.Seek(27, sprawl::filesystem::RelativeTo::CurrentPosition); EXPECT_EQ( sprawl::Format("{} [INFO] This is my message. [Test::Test3::Test4] (", sprawl::this_thread::GetHandle().GetUniqueId()) + sprawl::path::Basename(__FILE__) + sprawl::String(":") + sprawl::String(__FUNCTION__) + sprawl::String(":{}){}").format(line6 + 1, sprawl::filesystem::LineSeparator()), f.Read() ); f.Close(); f = sprawl::filesystem::Open("test3.log", "r"); //Skip timestamp. f.Seek(27, sprawl::filesystem::RelativeTo::Beginning); EXPECT_EQ( sprawl::Format("{} [INFO] This is my message. [Test::Test] (", sprawl::this_thread::GetHandle().GetUniqueId()) + sprawl::path::Basename(__FILE__) + sprawl::String(":") + sprawl::String(__FUNCTION__) + sprawl::String(":{}){}").format(line + 1, sprawl::filesystem::LineSeparator()), f.Read() ); f.Close(); f = sprawl::filesystem::Open("test4.log", "r"); //Skip timestamp. f.Seek(27, sprawl::filesystem::RelativeTo::Beginning); EXPECT_EQ( sprawl::Format("{} [INFO] This is my message. [Test::Test3] (", sprawl::this_thread::GetHandle().GetUniqueId()) + sprawl::path::Basename(__FILE__) + sprawl::String(":") + sprawl::String(__FUNCTION__) + sprawl::String(":{}){}").format(line5 + 1, sprawl::filesystem::LineSeparator()), f.ReadLine() ); //Skip timestamp. f.Seek(27, sprawl::filesystem::RelativeTo::CurrentPosition); EXPECT_EQ( sprawl::Format("{} [INFO] This is my message. [Test::Test3::Test4] (", sprawl::this_thread::GetHandle().GetUniqueId()) + sprawl::path::Basename(__FILE__) + sprawl::String(":") + sprawl::String(__FUNCTION__) + sprawl::String(":{}){}").format(line6 + 1, sprawl::filesystem::LineSeparator()), f.Read() ); f.Close(); f = sprawl::filesystem::Open("test5.log", "r"); //Skip timestamp. f.Seek(27, sprawl::filesystem::RelativeTo::CurrentPosition); EXPECT_EQ( sprawl::Format("{} [INFO] This is my message. [Test::Test3::Test4] (", sprawl::this_thread::GetHandle().GetUniqueId()) + sprawl::path::Basename(__FILE__) + sprawl::String(":") + sprawl::String(__FUNCTION__) + sprawl::String(":{}){}").format(line6 + 1, sprawl::filesystem::LineSeparator()), f.Read() ); f.Close(); EXPECT_TRUE(sprawl::filesystem::Remove("test.log")); EXPECT_TRUE(sprawl::filesystem::Remove("test2.log")); EXPECT_TRUE(sprawl::filesystem::Remove("test3.log")); EXPECT_TRUE(sprawl::filesystem::Remove("test4.log")); } TEST(LoggingTest, OptionsWork) { #ifdef _WIN32 int oldStdoutid = _dup(1); FILE* newStdout = fopen("stdout.log", "w"); _dup2(_fileno(newStdout), 1); #else FILE* oldStdout = stdout; stdout = fopen("stdout.log", "w"); #endif sprawl::logging::SetRenameMethod(sprawl::logging::RenameMethod::Counter, 5); sprawl::logging::SetDefaultHandler(sprawl::logging::PrintToFile("test.log")); sprawl::logging::Options infoOptions; infoOptions.logToStdout = true; infoOptions.nameOverride = "Information!"; sprawl::logging::SetLevelOptions(LogLevel::INFO, infoOptions); sprawl::logging::Init(); int line = __LINE__; LOG(INFO, sprawl::logging::Category("Test", "Test"), "This is my message."); LOG(DEBUG, sprawl::logging::Category("Test", "Test"), "This is also my message."); #ifdef _WIN32 fflush(stdout); fclose(newStdout); _dup2(oldStdoutid, 1); _close(oldStdoutid); #else fclose(stdout); stdout = oldStdout; #endif sprawl::logging::ShutDown(); ASSERT_TRUE(sprawl::path::Exists("test.log")); ASSERT_TRUE(sprawl::path::Exists("stdout.log")); sprawl::filesystem::File f = sprawl::filesystem::Open("test.log", "r"); //Skip timestamp. f.Seek(27, sprawl::filesystem::RelativeTo::Beginning); EXPECT_EQ( sprawl::Format("{} [Information!] This is my message. [Test::Test] (", sprawl::this_thread::GetHandle().GetUniqueId()) + sprawl::path::Basename(__FILE__) + sprawl::String(":") + sprawl::String(__FUNCTION__) + sprawl::String(":{}){}").format(line + 1, sprawl::filesystem::LineSeparator()), f.ReadLine() ); f.Seek(27, sprawl::filesystem::RelativeTo::CurrentPosition); EXPECT_EQ( sprawl::Format("{} [DEBUG] This is also my message. [Test::Test] (", sprawl::this_thread::GetHandle().GetUniqueId()) + sprawl::path::Basename(__FILE__) + sprawl::String(":") + sprawl::String(__FUNCTION__) + sprawl::String(":{}){}").format(line + 2, sprawl::filesystem::LineSeparator()), f.Read() ); f.Close(); f = sprawl::filesystem::Open("stdout.log", "r"); //Skip timestamp. f.Seek(27, sprawl::filesystem::RelativeTo::Beginning); EXPECT_EQ( sprawl::Format("{} [Information!] This is my message. [Test::Test] (", sprawl::this_thread::GetHandle().GetUniqueId()) + sprawl::path::Basename(__FILE__) + sprawl::String(":") + sprawl::String(__FUNCTION__) + sprawl::String(":{}){}").format(line + 1, sprawl::filesystem::LineSeparator()), f.Read() ); f.Close(); EXPECT_TRUE(sprawl::filesystem::Remove("test.log")); EXPECT_TRUE(sprawl::filesystem::Remove("stdout.log")); } TEST(LoggingTest, FileRecyclingWorks) { sprawl::logging::SetRenameMethod(sprawl::logging::RenameMethod::Counter, 5); sprawl::logging::SetDefaultHandler(sprawl::logging::PrintToFile("recycle.log")); //Win32 prints more info in __FUNCTION__ so it needs a larger buffer to get the same result #ifdef _WIN32 sprawl::logging::SetMaxFilesize(350); #else sprawl::logging::SetMaxFilesize(250); #endif sprawl::logging::Init(); int line = __LINE__; LOG(INFO, sprawl::logging::Category("Test", "Test"), "This is my message."); LOG(INFO, sprawl::logging::Category("Test", "Test"), "This is my message."); LOG(INFO, sprawl::logging::Category("Test", "Test"), "This is my message."); sprawl::logging::ShutDown(); ASSERT_TRUE(sprawl::path::Exists("recycle.log")); ASSERT_TRUE(sprawl::path::Exists("recycle.log.1")); sprawl::filesystem::File f = sprawl::filesystem::Open("recycle.log.1", "r"); //Skip timestamp. f.Seek(27, sprawl::filesystem::RelativeTo::Beginning); EXPECT_EQ( sprawl::Format("{} [INFO] This is my message. [Test::Test] (", sprawl::this_thread::GetHandle().GetUniqueId()) + sprawl::path::Basename(__FILE__) + sprawl::String(":") + sprawl::String(__FUNCTION__) + sprawl::String(":{}){}").format(line + 1, sprawl::filesystem::LineSeparator()), f.ReadLine() ); f.Seek(27, sprawl::filesystem::RelativeTo::CurrentPosition); EXPECT_EQ( sprawl::Format("{} [INFO] This is my message. [Test::Test] (", sprawl::this_thread::GetHandle().GetUniqueId()) + sprawl::path::Basename(__FILE__) + sprawl::String(":") + sprawl::String(__FUNCTION__) + sprawl::String(":{}){}").format(line + 2, sprawl::filesystem::LineSeparator()), f.Read() ); f.Close(); f = sprawl::filesystem::Open("recycle.log", "r"); //Skip timestamp. f.Seek(27, sprawl::filesystem::RelativeTo::Beginning); EXPECT_EQ( sprawl::Format("{} [INFO] This is my message. [Test::Test] (", sprawl::this_thread::GetHandle().GetUniqueId()) + sprawl::path::Basename(__FILE__) + sprawl::String(":") + sprawl::String(__FUNCTION__) + sprawl::String(":{}){}").format(line + 3, sprawl::filesystem::LineSeparator()), f.Read() ); f.Close(); EXPECT_TRUE(sprawl::filesystem::Remove("recycle.log")); EXPECT_TRUE(sprawl::filesystem::Remove("recycle.log.1")); } TEST(LoggingTest, CompileTimeLevelFilterWorks) { sprawl::logging::SetRenameMethod(sprawl::logging::RenameMethod::Counter, 5); sprawl::logging::SetDefaultHandler(sprawl::logging::PrintToFile("test.log")); sprawl::logging::Init(); bool executed = false; auto GetMessage = [&]() { executed = true; return "This is my message."; }; LOG(TRACE, sprawl::logging::Category("Test", "Test"), GetMessage()); EXPECT_FALSE(executed); sprawl::logging::ShutDown(); ASSERT_TRUE(sprawl::path::Exists("test.log")); sprawl::filesystem::File f = sprawl::filesystem::Open("test.log", "r"); EXPECT_EQ(0, f.FileSize()); EXPECT_EQ(sprawl::String(""), f.Read()); f.Close(); EXPECT_TRUE(sprawl::filesystem::Remove("test.log")); } TEST(LoggingTest, RunTimeLevelFilterWorks) { sprawl::logging::SetRenameMethod(sprawl::logging::RenameMethod::Counter, 5); sprawl::logging::SetDefaultHandler(sprawl::logging::PrintToFile("test.log")); sprawl::logging::SetRuntimeMinimumLevel(LogLevel::INFO); sprawl::logging::Init(); bool executed = false; auto GetMessage = [&]() { executed = true; return "This is my message."; }; LOG(DEBUG, sprawl::logging::Category("Test", "Test"), GetMessage()); EXPECT_FALSE(executed); sprawl::logging::ShutDown(); ASSERT_TRUE(sprawl::path::Exists("test.log")); sprawl::filesystem::File f = sprawl::filesystem::Open("test.log", "r"); EXPECT_EQ(0, f.FileSize()); EXPECT_EQ(sprawl::String(""), f.Read()); f.Close(); EXPECT_TRUE(sprawl::filesystem::Remove("test.log")); } TEST(LoggingTest, ExactCategoryFilterWorks) { sprawl::logging::SetRenameMethod(sprawl::logging::RenameMethod::Counter, 5); sprawl::logging::SetDefaultHandler(sprawl::logging::PrintToFile("test.log")); sprawl::logging::DisableCategory(sprawl::logging::Category("Test", "Test")); sprawl::logging::Init(); LOG(DEBUG, sprawl::logging::Category("Test", "Test"), "This is my message."); sprawl::logging::ShutDown(); ASSERT_TRUE(sprawl::path::Exists("test.log")); sprawl::filesystem::File f = sprawl::filesystem::Open("test.log", "r"); EXPECT_EQ(0, f.FileSize()); EXPECT_EQ(sprawl::String(""), f.Read()); f.Close(); EXPECT_TRUE(sprawl::filesystem::Remove("test.log")); } TEST(LoggingTest, ParentCategoryFilterWorks) { sprawl::logging::SetRenameMethod(sprawl::logging::RenameMethod::Counter, 5); sprawl::logging::SetDefaultHandler(sprawl::logging::PrintToFile("test.log")); sprawl::logging::DisableCategory(sprawl::logging::Category("Test")); sprawl::logging::Init(); LOG(DEBUG, sprawl::logging::Category("Test", "Test"), "This is my message."); sprawl::logging::ShutDown(); ASSERT_TRUE(sprawl::path::Exists("test.log")); sprawl::filesystem::File f = sprawl::filesystem::Open("test.log", "r"); EXPECT_EQ(0, f.FileSize()); EXPECT_EQ(sprawl::String(""), f.Read()); f.Close(); EXPECT_TRUE(sprawl::filesystem::Remove("test.log")); } #if !defined(_WIN32) || defined(_DEBUG) || SPRAWL_64_BIT TEST(LoggingTest, BacktracesPrintSomethingWhichIsToSayTheyPrintAnythingAtAll) { sprawl::logging::SetRenameMethod(sprawl::logging::RenameMethod::Counter, 5); sprawl::logging::SetDefaultHandler(sprawl::logging::PrintToFile("test.log")); sprawl::logging::Options options; options.includeBacktrace = true; sprawl::logging::SetLevelOptions(LogLevel::INFO, options); sprawl::logging::Init(); int line = __LINE__; LOG(INFO, sprawl::logging::Category("Test", "Test"), "This is my message."); sprawl::logging::ShutDown(); ASSERT_TRUE(sprawl::path::Exists("test.log")); sprawl::filesystem::File f = sprawl::filesystem::Open("test.log", "r"); //Skip timestamp. f.Seek(27, sprawl::filesystem::RelativeTo::Beginning); EXPECT_EQ( sprawl::Format("{} [INFO] This is my message. [Test::Test] (", sprawl::this_thread::GetHandle().GetUniqueId()) + sprawl::path::Basename(__FILE__) + sprawl::String(":") + sprawl::String(__FUNCTION__) + sprawl::String(":{}){}").format(line + 1, sprawl::filesystem::LineSeparator()), f.ReadLine() ); //Not bothering to try and PARSE backtraces, let's just make sure there's more in the file than this one line. EXPECT_LT(f.Tell(), f.FileSize()); puts(f.Read().c_str()); f.Close(); EXPECT_TRUE(sprawl::filesystem::Remove("test.log")); } #endif TEST(LoggingTest, LogIfWorks) { sprawl::logging::SetRenameMethod(sprawl::logging::RenameMethod::Counter, 5); sprawl::logging::SetDefaultHandler(sprawl::logging::PrintToFile("test.log")); sprawl::logging::Init(); LOG_IF(false, INFO, sprawl::logging::Category("Test", "Test"), "This is not my message."); int line = __LINE__; LOG_IF(true, INFO, sprawl::logging::Category("Test", "Test"), "This is my message."); sprawl::logging::ShutDown(); ASSERT_TRUE(sprawl::path::Exists("test.log")); sprawl::filesystem::File f = sprawl::filesystem::Open("test.log", "r"); //Skip timestamp. f.Seek(27, sprawl::filesystem::RelativeTo::Beginning); EXPECT_EQ( sprawl::Format("{} [INFO] This is my message. [Test::Test] (", sprawl::this_thread::GetHandle().GetUniqueId()) + sprawl::path::Basename(__FILE__) + sprawl::String(":") + sprawl::String(__FUNCTION__) + sprawl::String(":{}){}").format(line + 1, sprawl::filesystem::LineSeparator()), f.Read() ); f.Close(); EXPECT_TRUE(sprawl::filesystem::Remove("test.log")); } TEST(LoggingTest, LogEveryNWorks) { sprawl::logging::SetRenameMethod(sprawl::logging::RenameMethod::Counter, 5); sprawl::logging::SetDefaultHandler(sprawl::logging::PrintToFile("test.log")); sprawl::logging::Init(); int line = __LINE__; for(int i = 0; i < 20; ++i) { LOG_EVERY_N(5, INFO, sprawl::logging::Category("Test", "Test"), sprawl::Format("This is my message: {}", i)); } sprawl::logging::ShutDown(); ASSERT_TRUE(sprawl::path::Exists("test.log")); sprawl::filesystem::File f = sprawl::filesystem::Open("test.log", "r"); //Skip timestamp. f.Seek(27, sprawl::filesystem::RelativeTo::Beginning); EXPECT_EQ( sprawl::Format("{} [INFO] This is my message: 0 [Test::Test] (", sprawl::this_thread::GetHandle().GetUniqueId()) + sprawl::path::Basename(__FILE__) + sprawl::String(":") + sprawl::String(__FUNCTION__) + sprawl::String(":{}){}").format(line + 3, sprawl::filesystem::LineSeparator()), f.ReadLine() ); f.Seek(27, sprawl::filesystem::RelativeTo::CurrentPosition); EXPECT_EQ( sprawl::Format("{} [INFO] This is my message: 5 [Test::Test] (", sprawl::this_thread::GetHandle().GetUniqueId()) + sprawl::path::Basename(__FILE__) + sprawl::String(":") + sprawl::String(__FUNCTION__) + sprawl::String(":{}){}").format(line + 3, sprawl::filesystem::LineSeparator()), f.ReadLine() ); f.Seek(27, sprawl::filesystem::RelativeTo::CurrentPosition); EXPECT_EQ( sprawl::Format("{} [INFO] This is my message: 10 [Test::Test] (", sprawl::this_thread::GetHandle().GetUniqueId()) + sprawl::path::Basename(__FILE__) + sprawl::String(":") + sprawl::String(__FUNCTION__) + sprawl::String(":{}){}").format(line + 3, sprawl::filesystem::LineSeparator()), f.ReadLine() ); f.Seek(27, sprawl::filesystem::RelativeTo::CurrentPosition); EXPECT_EQ( sprawl::Format("{} [INFO] This is my message: 15 [Test::Test] (", sprawl::this_thread::GetHandle().GetUniqueId()) + sprawl::path::Basename(__FILE__) + sprawl::String(":") + sprawl::String(__FUNCTION__) + sprawl::String(":{}){}").format(line + 3, sprawl::filesystem::LineSeparator()), f.Read() ); f.Close(); EXPECT_TRUE(sprawl::filesystem::Remove("test.log")); } TEST(LoggingTest, LogIfEveryNWorks) { sprawl::logging::SetRenameMethod(sprawl::logging::RenameMethod::Counter, 5); sprawl::logging::SetDefaultHandler(sprawl::logging::PrintToFile("test.log")); sprawl::logging::Init(); int line = __LINE__; for(int i = 0; i < 20; ++i) { LOG_IF_EVERY_N(i % 10 == 0, 5, INFO, sprawl::logging::Category("Test", "Test"), sprawl::Format("This is my message: {}", i)); } sprawl::logging::ShutDown(); ASSERT_TRUE(sprawl::path::Exists("test.log")); sprawl::filesystem::File f = sprawl::filesystem::Open("test.log", "r"); //Skip timestamp. f.Seek(27, sprawl::filesystem::RelativeTo::Beginning); EXPECT_EQ( sprawl::Format("{} [INFO] This is my message: 0 [Test::Test] (", sprawl::this_thread::GetHandle().GetUniqueId()) + sprawl::path::Basename(__FILE__) + sprawl::String(":") + sprawl::String(__FUNCTION__) + sprawl::String(":{}){}").format(line + 3, sprawl::filesystem::LineSeparator()), f.ReadLine() ); f.Seek(27, sprawl::filesystem::RelativeTo::CurrentPosition); EXPECT_EQ( sprawl::Format("{} [INFO] This is my message: 10 [Test::Test] (", sprawl::this_thread::GetHandle().GetUniqueId()) + sprawl::path::Basename(__FILE__) + sprawl::String(":") + sprawl::String(__FUNCTION__) + sprawl::String(":{}){}").format(line + 3, sprawl::filesystem::LineSeparator()), f.Read() ); f.Close(); EXPECT_TRUE(sprawl::filesystem::Remove("test.log")); } TEST(LoggingTest, LogFirstNWorks) { sprawl::logging::SetRenameMethod(sprawl::logging::RenameMethod::Counter, 5); sprawl::logging::SetDefaultHandler(sprawl::logging::PrintToFile("test.log")); sprawl::logging::Init(); int line = __LINE__; for(int i = 0; i < 20; ++i) { LOG_FIRST_N(5, INFO, sprawl::logging::Category("Test", "Test"), sprawl::Format("This is my message: {}", i)); } sprawl::logging::ShutDown(); ASSERT_TRUE(sprawl::path::Exists("test.log")); sprawl::filesystem::File f = sprawl::filesystem::Open("test.log", "r"); //Skip timestamp. f.Seek(27, sprawl::filesystem::RelativeTo::Beginning); EXPECT_EQ( sprawl::Format("{} [INFO] This is my message: 0 [Test::Test] (", sprawl::this_thread::GetHandle().GetUniqueId()) + sprawl::path::Basename(__FILE__) + sprawl::String(":") + sprawl::String(__FUNCTION__) + sprawl::String(":{}){}").format(line + 3, sprawl::filesystem::LineSeparator()), f.ReadLine() ); f.Seek(27, sprawl::filesystem::RelativeTo::CurrentPosition); EXPECT_EQ( sprawl::Format("{} [INFO] This is my message: 1 [Test::Test] (", sprawl::this_thread::GetHandle().GetUniqueId()) + sprawl::path::Basename(__FILE__) + sprawl::String(":") + sprawl::String(__FUNCTION__) + sprawl::String(":{}){}").format(line + 3, sprawl::filesystem::LineSeparator()), f.ReadLine() ); f.Seek(27, sprawl::filesystem::RelativeTo::CurrentPosition); EXPECT_EQ( sprawl::Format("{} [INFO] This is my message: 2 [Test::Test] (", sprawl::this_thread::GetHandle().GetUniqueId()) + sprawl::path::Basename(__FILE__) + sprawl::String(":") + sprawl::String(__FUNCTION__) + sprawl::String(":{}){}").format(line + 3, sprawl::filesystem::LineSeparator()), f.ReadLine() ); f.Seek(27, sprawl::filesystem::RelativeTo::CurrentPosition); EXPECT_EQ( sprawl::Format("{} [INFO] This is my message: 3 [Test::Test] (", sprawl::this_thread::GetHandle().GetUniqueId()) + sprawl::path::Basename(__FILE__) + sprawl::String(":") + sprawl::String(__FUNCTION__) + sprawl::String(":{}){}").format(line + 3, sprawl::filesystem::LineSeparator()), f.ReadLine() ); f.Seek(27, sprawl::filesystem::RelativeTo::CurrentPosition); EXPECT_EQ( sprawl::Format("{} [INFO] This is my message: 4 [Test::Test] (", sprawl::this_thread::GetHandle().GetUniqueId()) + sprawl::path::Basename(__FILE__) + sprawl::String(":") + sprawl::String(__FUNCTION__) + sprawl::String(":{}){}").format(line + 3, sprawl::filesystem::LineSeparator()), f.Read() ); f.Close(); EXPECT_TRUE(sprawl::filesystem::Remove("test.log")); } TEST(LoggingTest, LogIfFirstNWorks) { sprawl::logging::SetRenameMethod(sprawl::logging::RenameMethod::Counter, 5); sprawl::logging::SetDefaultHandler(sprawl::logging::PrintToFile("test.log")); sprawl::logging::Init(); int line = __LINE__; for(int i = 0; i < 20; ++i) { LOG_IF_FIRST_N(i % 2 == 0, 5, INFO, sprawl::logging::Category("Test", "Test"), sprawl::Format("This is my message: {}", i)); } sprawl::logging::ShutDown(); ASSERT_TRUE(sprawl::path::Exists("test.log")); sprawl::filesystem::File f = sprawl::filesystem::Open("test.log", "r"); //Skip timestamp. f.Seek(27, sprawl::filesystem::RelativeTo::Beginning); EXPECT_EQ( sprawl::Format("{} [INFO] This is my message: 0 [Test::Test] (", sprawl::this_thread::GetHandle().GetUniqueId()) + sprawl::path::Basename(__FILE__) + sprawl::String(":") + sprawl::String(__FUNCTION__) + sprawl::String(":{}){}").format(line + 3, sprawl::filesystem::LineSeparator()), f.ReadLine() ); f.Seek(27, sprawl::filesystem::RelativeTo::CurrentPosition); EXPECT_EQ( sprawl::Format("{} [INFO] This is my message: 2 [Test::Test] (", sprawl::this_thread::GetHandle().GetUniqueId()) + sprawl::path::Basename(__FILE__) + sprawl::String(":") + sprawl::String(__FUNCTION__) + sprawl::String(":{}){}").format(line + 3, sprawl::filesystem::LineSeparator()), f.ReadLine() ); f.Seek(27, sprawl::filesystem::RelativeTo::CurrentPosition); EXPECT_EQ( sprawl::Format("{} [INFO] This is my message: 4 [Test::Test] (", sprawl::this_thread::GetHandle().GetUniqueId()) + sprawl::path::Basename(__FILE__) + sprawl::String(":") + sprawl::String(__FUNCTION__) + sprawl::String(":{}){}").format(line + 3, sprawl::filesystem::LineSeparator()), f.ReadLine() ); f.Seek(27, sprawl::filesystem::RelativeTo::CurrentPosition); EXPECT_EQ( sprawl::Format("{} [INFO] This is my message: 6 [Test::Test] (", sprawl::this_thread::GetHandle().GetUniqueId()) + sprawl::path::Basename(__FILE__) + sprawl::String(":") + sprawl::String(__FUNCTION__) + sprawl::String(":{}){}").format(line + 3, sprawl::filesystem::LineSeparator()), f.ReadLine() ); f.Seek(27, sprawl::filesystem::RelativeTo::CurrentPosition); EXPECT_EQ( sprawl::Format("{} [INFO] This is my message: 8 [Test::Test] (", sprawl::this_thread::GetHandle().GetUniqueId()) + sprawl::path::Basename(__FILE__) + sprawl::String(":") + sprawl::String(__FUNCTION__) + sprawl::String(":{}){}").format(line + 3, sprawl::filesystem::LineSeparator()), f.Read() ); f.Close(); EXPECT_TRUE(sprawl::filesystem::Remove("test.log")); } TEST(LoggingDeathTest, LogAssertWorks) { //Don't actually care about it being threadsafe, this is to silence a stupid warning that won't go away otherwise on linux. ::testing::FLAGS_gtest_death_test_style = "threadsafe"; sprawl::logging::SetRenameMethod(sprawl::logging::RenameMethod::Counter, 5); sprawl::logging::SetDefaultHandler(sprawl::logging::PrintToStderr()); sprawl::logging::Init(); EXPECT_DEATH(LOG_ASSERT(false, INFO, sprawl::logging::Category("Test", "Test"), "I'M DEAD!"), "Assertion failed: .*"); EXPECT_DEATH(LOG_ASSERT(false, INFO, sprawl::logging::Category("Test", "Test"), "I'M DEAD!"), "I'M DEAD!"); EXPECT_EXIT( { LOG_ASSERT(true, INFO, sprawl::logging::Category("Test", "Test"), "I'M DEAD!"); LOG(INFO, sprawl::logging::Category("Test", "Test"), "I'm alive!"); exit(0); }, testing::ExitedWithCode(0), "I'm alive!" ); sprawl::logging::ShutDown(); } void* GetMessage_() { return (void*)"Extra error info"; } void PrintMessage(void* message, sprawl::StringBuilder& builder) { builder << (char const* const)(message); } TEST(LoggingTest, ExtraInfoWorks) { sprawl::logging::SetRenameMethod(sprawl::logging::RenameMethod::Counter, 5); sprawl::logging::SetDefaultHandler(sprawl::logging::PrintToFile("test.log")); sprawl::logging::AddExtraInfoCallback(LogLevel::INFO, GetMessage_, PrintMessage); sprawl::logging::Init(); int line = __LINE__; LOG(INFO, sprawl::logging::Category("Test", "Test"), "This is my message."); sprawl::logging::ShutDown(); ASSERT_TRUE(sprawl::path::Exists("test.log")); sprawl::filesystem::File f = sprawl::filesystem::Open("test.log", "r"); //Skip timestamp. f.Seek(27, sprawl::filesystem::RelativeTo::Beginning); EXPECT_EQ( sprawl::Format("{} [INFO] This is my message. [Test::Test] (", sprawl::this_thread::GetHandle().GetUniqueId()) + sprawl::path::Basename(__FILE__) + sprawl::String(":") + sprawl::String(__FUNCTION__) + sprawl::String(":{}){}").format(line + 1, sprawl::filesystem::LineSeparator()), f.ReadLine() ); EXPECT_EQ( sprawl::Format("Extra error info{}", sprawl::filesystem::LineSeparator()), f.Read() ); f.Close(); EXPECT_TRUE(sprawl::filesystem::Remove("test.log")); }
# | Change | User | Description | Committed | |
---|---|---|---|---|---|
#6 | 16768 | ShadauxCat |
Improvements to error handling in builds with exceptions disabled: - In debug builds or with SPRAWL_ERRORSTATE_STRICT enabled, ErrorState will output a message to stderr and terminate if Get() is called when an error flag is set. (In release buils or with SPRAWL_ERRORSTATE_PERMISSIVE defined, Get() will return junk memory in this case.) - In debug builds or with SPRAWL_ERRORSTATE_STRICT enabled, ErrorState will output a message to stderr and terminate if its destructor is called without checking the errorstate if an error is present (equivalent to an exception terminating the application if no catch() block is present for it). - On linux builds and when running "Analyze" through visual studio, a warning will be issued if any function returning ErrorState has its return value ignored. (This only applies to builds with exceptions not enabled; when exceptions are enabled no warning is issued) - Many functions that could return ErrorState were having their return values silently ignored in internal sprawl code so the user would not find out about errors if exceptions are disabled; now anything in sprawl code that calls a function returning ErrorState will either handle the error, or (in most cases) surface it back up to the user. - As a positive side-effect of the warnings for ignoring ErrorState, several constructors that were capable of throwing exceptions are no longer capable of doing so. #review-16769 |
||
#5 | 16135 | ShadauxCat |
- Changed the way category handlers are registered a bit so that a category can't get the same handler added to it twice (i.e., from having both a parent and a grandparent with the same handler, which would happen if, for example, "Test" were registered with a handler, then "Test::Test2" were set to inherite handlers from "Test", and then "Test::Test2::Test3" were set to inherit from "Test::Test2") - Changed levelStr parameter to log function to be a char* so there's one less string to construct - Moved runtime level checking up higher so that arguments wouldn't be called/constructed if the log level is runtime-disabled - Added unit tests to verify the above #review-16136 |
||
#4 | 16068 | ShadauxCat |
Disabled backtrace unit test for Release / x86 on windows. CaptureStackTrace returns 0 frames on optimized x86 builds - no way around that. Optimization usually kills backtraces anyway, and x86 is a disappearing platform, so I'm choosing not to care that release/x86 doesn't work for backtraces. #review-16069 |
||
#3 | 16052 | ShadauxCat |
- Changed default block size for concurrent queue to a more reasonable value - Changed some memory orders to memory_order_seq_cst when they don't actually need to be that to get around a bug in visual studio 2013 - debug builds assert when memory_order_acq_rel is used for a compare_exchange_strong (this is a standard library bug and is fixed in VS2015) - Added Event API - events are an alternative to condition variables that do not require a mutex and are guaranteed not to miss any signals, even if the signal comes while the thread is not listening for it. Unlike condition variables, however, they do not support broadcasting (and in fact, in general, are not safe to use with multiple threads listening for the same event simultaneously - though notifying on the same event is fine) - Rewrote ThreadManager around ConcurrentQueue and Event API so it is now lock-free. Also improved some behaviors of the staged thread manager operation so it now supports tasks that can be run on multiple stages via a bitmask. - Fixed an issue where the Coroutine copy constructor was calling the std::function constructor instead and another where initializing with a stack might try to call the wrong constructor and vice-versa - Fixed Coroutine never calling munmap() on its stack in linux and causing a memory leak - Added default arguments to time functions - Attempted to fix some issues with BinaryTree. Fixed some but not all. It's currently not suitable for use, sadly. - Logging Improvements: - - Added thread ID to logging - - Fixed some issues with category handlers - - Added backtraces - - Added the following additional log macros: - - - LOG_IF - - - LOG_EVERY_N - - - LOG_FIRST_N - - - LOG_IF_EVERY_N - - - LOG_IF_FIRST_N - - - LOG_ASSERT - - Added the ability to set extra info callbacks to get data such as script backtraces - - Removed the thread-related handlers and replaced them with RunHandler_Threaded and RunHandler_ThreadManager, which will enable any passed-in handler to be run in a threaded fashion - Removed StaticPoolAllocator and renamed DynamicPoolAllocator to PoolAllocator; adjusted unit tests accordingly - PoolAllocator now allocates its pool with mmap and VirtualAlloc, rather than with malloc - Fixed a bug with Vector copy assignment operator - Improved performance of StringBuilder considerably for cases where there are no modifier strings - Removed Copy-On-Write behavior of JSONToken as it was broken; copies are now performed with explicit DeepCopy() and ShallowCopy() functions - Fixed some parser bugs with JSONToken - Added iteration to JSONToken to iterate its children - Fixed crash when reading a negative number of bytes from a file - Changed StringBuilder to favor speed instead of memory by default - Added some performance unit tests for JSON token #review-16053 |
||
#2 | 14835 | ShadauxCat |
Fixing compile errors in Linux that were introduced by fixing compile errors in Windows. #review-14836 |
||
#1 | 14833 | ShadauxCat |
First checkin of logging module. Also fixes the following issues: -Added UpperBound() and LowerBound() to BinaryTree and created appropriate unit tests -Added Sync() to ThreadManager to force it to run all tasks to completion and not return until it has no tasks left -Fixed a bug in String::format() where a non-numeric value inside {} would be treated as an empty {}; it now simply prints whatever the value was. (i.e., "{blah}".format(foo) simply returns "{blah}") -Added Reset() to sprawl::StringBuilder -Disabled the switch-enum warning flag in gcc because it's stupid and ridiculous that a default case doesn't shut it up -Made sprawl::Mutex movable. This may turn out to be a bad idea but it enabled keeping them in a map. -Fixed a name collission between HashMap and BinaryTree; both defined sprawl::collections::detail::UnderlyingType and ::MethodType. Prefixed the ones in BinaryTree with "Tree". This isn't the best solution, but it works for now. #review-14834 |