diff --git a/CMakeLists.txt b/CMakeLists.txt index a87b232..5caad12 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -9,6 +9,7 @@ endif() project(webcc) option(WEBCC_ENABLE_SOAP "Enable SOAP support (need pugixml)?" ON) +option(WEBCC_ENABLE_TEST "Build test?" ON) option(WEBCC_ENABLE_UNITTEST "Build unit test?" ON) option(WEBCC_ENABLE_EXAMPLES "Build examples?" ON) @@ -140,6 +141,10 @@ if(WEBCC_ENABLE_EXAMPLES) add_subdirectory(examples) endif() +if(WEBCC_ENABLE_TEST) + add_subdirectory(test) +endif() + if(WEBCC_ENABLE_UNITTEST) add_subdirectory(${THIRD_PARTY_DIR}/src/gtest) add_subdirectory(unittest) diff --git a/_clang-format b/_clang-format index 255c975..bb15149 100644 --- a/_clang-format +++ b/_clang-format @@ -20,7 +20,7 @@ AlwaysBreakBeforeMultilineStrings: true AlwaysBreakTemplateDeclarations: Yes BinPackArguments: true BinPackParameters: true -BraceWrapping: +BraceWrapping: AfterClass: false AfterControlStatement: false AfterEnum: false @@ -56,12 +56,12 @@ DerivePointerAlignment: false DisableFormat: false ExperimentalAutoDetectBinPacking: false FixNamespaceComments: true -ForEachMacros: +ForEachMacros: - foreach - Q_FOREACH - BOOST_FOREACH IncludeBlocks: Preserve -IncludeCategories: +IncludeCategories: - Regex: '^' Priority: 2 - Regex: '^<.*\.h>' @@ -95,9 +95,9 @@ PenaltyBreakTemplateDeclaration: 10 PenaltyExcessCharacter: 1000000 PenaltyReturnTypeOnItsOwnLine: 200 PointerAlignment: Left -RawStringFormats: +RawStringFormats: - Language: Cpp - Delimiters: + Delimiters: - cc - CC - cpp @@ -108,12 +108,12 @@ RawStringFormats: CanonicalDelimiter: '' BasedOnStyle: google - Language: TextProto - Delimiters: + Delimiters: - pb - PB - proto - PROTO - EnclosingFunctions: + EnclosingFunctions: - EqualsProto - EquivToProto - PARSE_PARTIAL_TEXT_PROTO diff --git a/doc/screenshots/logger_colors.png b/doc/screenshots/logger_colors.png new file mode 100644 index 0000000..993db1b Binary files /dev/null and b/doc/screenshots/logger_colors.png differ diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt new file mode 100644 index 0000000..627359b --- /dev/null +++ b/test/CMakeLists.txt @@ -0,0 +1,19 @@ +# Tests + +# Common libraries to link. +set(TEST_COMMON_LIBS webcc ${Boost_LIBRARIES} ${OPENSSL_LIBRARIES} + "${CMAKE_THREAD_LIBS_INIT}") +if(WIN32) + set(TEST_COMMON_LIBS ${TEST_COMMON_LIBS} zlibstatic crypt32) +else() + set(TEST_COMMON_LIBS ${TEST_COMMON_LIBS} ${ZLIB_LIBRARIES}) +endif() + +if(UNIX) + # Add `-ldl` for Linux to avoid "undefined reference to `dlopen'". + set(TEST_COMMON_LIBS ${TEST_COMMON_LIBS} ${CMAKE_DL_LIBS}) +endif() + +add_executable(test_logger test_logger.cc) +target_link_libraries(test_logger ${TEST_COMMON_LIBS}) + diff --git a/test/test_logger.cc b/test/test_logger.cc new file mode 100644 index 0000000..ecb5436 --- /dev/null +++ b/test/test_logger.cc @@ -0,0 +1,15 @@ +#include "webcc/logger.h" + +int main() { + WEBCC_LOG_INIT("", webcc::LOG_CONSOLE); + + LOG_INFO("info"); + LOG_WARN("warn"); + LOG_ERRO("erro"); + LOG_VERB("verb"); + LOG_FATA("fata"); + LOG_INFO("info"); + + return 0; +} + diff --git a/webcc/http_request_builder.cc b/webcc/http_request_builder.cc index a0a9fc1..a46e8d1 100644 --- a/webcc/http_request_builder.cc +++ b/webcc/http_request_builder.cc @@ -43,7 +43,7 @@ HttpRequestPtr HttpRequestBuilder::Build() { std::string data; CreateFormData(&data, boundary); - // Ingore gzip since most servers don't support it. + // Ingore gzip since most servers don't support it. request->SetContent(std::move(data), true); } diff --git a/webcc/logger.cc b/webcc/logger.cc index bd5bab7..fa2971a 100644 --- a/webcc/logger.cc +++ b/webcc/logger.cc @@ -24,6 +24,16 @@ namespace webcc { +// ----------------------------------------------------------------------------- + +static std::string g_main_thread_id; + +static const char* kLevelNames[] = { + "VERB", "INFO", "WARN", "ERRO", "FATA" +}; + +// ----------------------------------------------------------------------------- + struct Logger { Logger() : file(nullptr), modes(0) { } @@ -56,11 +66,96 @@ struct Logger { // Global logger. static Logger g_logger; -static std::string g_main_thread_id; +// ----------------------------------------------------------------------------- -static const char* kLevelNames[] = { - "VERB", "INFO", "WARN", "ERRO", "FATA" -}; +// The colors related code was adapted from Loguru. See: +// https://github.com/emilk/loguru/blob/master/loguru.cpp +// Thanks to Loguru! + +bool g_colorlogtostderr = true; + +static const bool g_terminal_has_color = []() { +#if (defined(WIN32) || defined(_WIN64)) +#ifndef ENABLE_VIRTUAL_TERMINAL_PROCESSING +#define ENABLE_VIRTUAL_TERMINAL_PROCESSING 0x0004 +#endif + HANDLE houtput = GetStdHandle(STD_OUTPUT_HANDLE); + if (houtput != INVALID_HANDLE_VALUE) { + DWORD mode = 0; + GetConsoleMode(houtput, &mode); + mode |= ENABLE_VIRTUAL_TERMINAL_PROCESSING; + return SetConsoleMode(houtput, mode) != 0; + } + return false; +#else + const char* term = getenv("TERM"); + if (term == nullptr) { + return false; + } + + return strcmp(term, "cygwin") == 0 || strcmp(term, "linux") == 0 || + strcmp(term, "rxvt-unicode-256color") == 0 || + strcmp(term, "screen") == 0 || strcmp(term, "screen-256color") == 0 || + strcmp(term, "tmux-256color") == 0 || strcmp(term, "xterm") == 0 || + strcmp(term, "xterm-256color") == 0 || + strcmp(term, "xterm-termite") == 0 || strcmp(term, "xterm-color") == 0; +#endif +}(); + +// Colors + +#ifdef _WIN32 +#define VTSEQ(ID) ("\x1b[1;" #ID "m") +#else +#define VTSEQ(ID) ("\x1b[" #ID "m") +#endif + +const char* TerminalBlack() { + return g_terminal_has_color ? VTSEQ(30) : ""; +} +const char* TerminalRed() { + return g_terminal_has_color ? VTSEQ(31) : ""; +} +const char* TerminalGreen() { + return g_terminal_has_color ? VTSEQ(32) : ""; +} +const char* TerminalYellow() { + return g_terminal_has_color ? VTSEQ(33) : ""; +} +const char* TerminalBlue() { + return g_terminal_has_color ? VTSEQ(34) : ""; +} +const char* TerminalPurple() { + return g_terminal_has_color ? VTSEQ(35) : ""; +} +const char* TerminalCyan() { + return g_terminal_has_color ? VTSEQ(36) : ""; +} +const char* TerminalLightGray() { + return g_terminal_has_color ? VTSEQ(37) : ""; +} +const char* TerminalWhite() { + return g_terminal_has_color ? VTSEQ(37) : ""; +} +const char* TerminalLightRed() { + return g_terminal_has_color ? VTSEQ(91) : ""; +} +const char* TerminalDim() { + return g_terminal_has_color ? VTSEQ(2) : ""; +} + +// Formating +const char* TerminalBold() { + return g_terminal_has_color ? VTSEQ(1) : ""; +} +const char* TerminalUnderline() { + return g_terminal_has_color ? VTSEQ(4) : ""; +} + +// You should end each line with this! +const char* TerminalReset() { + return g_terminal_has_color ? VTSEQ(0) : ""; +} namespace bfs = boost::filesystem; @@ -123,8 +218,8 @@ static std::string GetTimestamp() { ss << std::put_time(std::localtime(&t), "%Y-%m-%d %H:%M:%S"); std::chrono::milliseconds milli_seconds = - std::chrono::duration_cast( - now.time_since_epoch()); + std::chrono::duration_cast( + now.time_since_epoch()); std::string micro_seconds_str = std::to_string(milli_seconds.count() % 1000); while (micro_seconds_str.size() < 3) { micro_seconds_str = "0" + micro_seconds_str; @@ -168,13 +263,33 @@ void LogWrite(int level, const char* file, int line, const char* format, ...) { va_list args; va_start(args, format); - fprintf(stderr, "%s, %s, %7s, %25s, %4d, ", - timestamp.c_str(), kLevelNames[level], thread_id.c_str(), - file, line); + if (g_colorlogtostderr && g_terminal_has_color) { + if (level < WEBCC_WARN) { + fprintf(stderr, "%s%s%s, %s, %7s, %25s, %4d, %s", + TerminalReset(), TerminalDim(), + timestamp.c_str(), kLevelNames[level], thread_id.c_str(), + file, line, + level == WEBCC_INFO ? TerminalReset() : ""); // un-dim for INFO + } else { + fprintf(stderr, "%s%s%s, %s, %7s, %25s, %4d, ", + TerminalReset(), + level == WEBCC_WARN ? TerminalYellow() : TerminalRed(), + timestamp.c_str(), kLevelNames[level], thread_id.c_str(), + file, line); + } - vfprintf(stderr, format, args); + vfprintf(stderr, format, args); - fprintf(stderr, "\n"); + fprintf(stderr, "%s\n", TerminalReset()); + } else { + fprintf(stderr, "%s, %s, %7s, %25s, %4d, ", + timestamp.c_str(), kLevelNames[level], thread_id.c_str(), + file, line); + + vfprintf(stderr, format, args); + + fprintf(stderr, "\n"); + } if ((g_logger.modes & LOG_FLUSH) != 0) { fflush(stderr);