diff --git a/CMakeLists.txt b/CMakeLists.txt index 4715980..2ec5cef 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -68,24 +68,6 @@ if(WIN32) add_definitions(-D_SCL_SECURE_NO_WARNINGS) endif() -# Group sources by dir. -# Usage: source_group_by_dir(SRCS) -macro(GROUP_SOURCES_BY_DIR source_files) - set(sgbd_cur_dir ${CMAKE_CURRENT_BINARY_DIR}) - foreach(sgbd_file ${${source_files}}) - #message("sgbd_fpath=${sgbd_fpath}") - string(REGEX REPLACE ${sgbd_cur_dir}/\(.*\) \\1 sgbd_fpath ${sgbd_file}) - string(REGEX REPLACE "\(.*\)/.*" \\1 sgbd_group_name ${sgbd_fpath}) - string(COMPARE EQUAL ${sgbd_fpath} ${sgbd_group_name} sgbd_nogroup) - string(REPLACE "/" "\\" sgbd_group_name ${sgbd_group_name}) - if(sgbd_nogroup) - set(sgbd_group_name "\\") - endif() - #message("group name=${sgbd_group_name}") - source_group(${sgbd_group_name} FILES ${sgbd_file}) - endforeach() -endmacro() - # C++ standard requirements. set(CMAKE_CXX_STANDARD 11) set(CMAKE_CXX_STANDARD_REQUIRED ON) diff --git a/example/http/async_client/main.cc b/example/http/async_client/main.cc index 6516cb1..51e215c 100644 --- a/example/http/async_client/main.cc +++ b/example/http/async_client/main.cc @@ -40,7 +40,7 @@ void Test(boost::asio::io_context& ioc) { } int main() { - LOG_INIT(0); + LOG_INIT("", webcc::LOG_CONSOLE); boost::asio::io_context ioc; diff --git a/example/http/client/main.cc b/example/http/client/main.cc index 83a2cf0..33e87ca 100644 --- a/example/http/client/main.cc +++ b/example/http/client/main.cc @@ -28,7 +28,7 @@ void Test() { } int main() { - LOG_INIT(0); + LOG_INIT("", webcc::LOG_CONSOLE); Test(); Test(); diff --git a/example/rest/book_async_client/main.cc b/example/rest/book_async_client/main.cc index 51b0ceb..2671a8f 100644 --- a/example/rest/book_async_client/main.cc +++ b/example/rest/book_async_client/main.cc @@ -101,7 +101,7 @@ int main(int argc, char* argv[]) { return 1; } - LOG_INIT(0); + LOG_INIT("", webcc::LOG_CONSOLE); std::string host = argv[1]; std::string port = argv[2]; diff --git a/example/rest/book_client/main.cc b/example/rest/book_client/main.cc index e24db3a..331b553 100644 --- a/example/rest/book_client/main.cc +++ b/example/rest/book_client/main.cc @@ -160,7 +160,7 @@ int main(int argc, char* argv[]) { return 1; } - LOG_INIT(0); + LOG_INIT("", webcc::LOG_CONSOLE); std::string host = argv[1]; std::string port = argv[2]; diff --git a/example/rest/book_server/main.cc b/example/rest/book_server/main.cc index 4c40b31..f523e7b 100644 --- a/example/rest/book_server/main.cc +++ b/example/rest/book_server/main.cc @@ -21,7 +21,7 @@ int main(int argc, char* argv[]) { return 1; } - LOG_INIT(0); + LOG_INIT("", webcc::LOG_FILE | webcc::LOG_CONSOLE | webcc::LOG_OVERWRITE); unsigned short port = std::atoi(argv[1]); diff --git a/example/soap/calc_client/main.cc b/example/soap/calc_client/main.cc index 095cd46..85c493e 100644 --- a/example/soap/calc_client/main.cc +++ b/example/soap/calc_client/main.cc @@ -1,9 +1,11 @@ #include + #include "webcc/logger.h" + #include "calc_client.h" int main() { - LOG_INIT(0); + LOG_INIT("", webcc::LOG_CONSOLE); CalcClient calc; diff --git a/example/soap/calc_server/main.cc b/example/soap/calc_server/main.cc index e1f3920..2c8fb2f 100644 --- a/example/soap/calc_server/main.cc +++ b/example/soap/calc_server/main.cc @@ -17,7 +17,7 @@ int main(int argc, char* argv[]) { return 1; } - LOG_INIT(0); + LOG_INIT("", webcc::LOG_CONSOLE); unsigned short port = std::atoi(argv[1]); diff --git a/src/webcc/logger.cc b/src/webcc/logger.cc index d2b7329..e7838ac 100644 --- a/src/webcc/logger.cc +++ b/src/webcc/logger.cc @@ -6,20 +6,41 @@ #include #include #include +#include #include #include #include #include +#include "boost/filesystem.hpp" + namespace webcc { struct Logger { + Logger(const std::string& path, int modes) : file(nullptr), modes(modes) { + if (!path.empty()) { + if ((modes & LOG_OVERWRITE) != 0) { + file = fopen(path.c_str(), "w+"); + } else { + // Append to existing file. + file = fopen(path.c_str(), "a+"); + } + } + } + + ~Logger() { + if (file != nullptr) { + fclose(file); + } + } + + FILE* file; int modes; std::mutex mutex; }; // Global logger. -static Logger g_logger{ 0 }; +static std::shared_ptr g_logger; static std::thread::id g_main_thread_id; @@ -27,8 +48,28 @@ static const char* kLevelNames[] = { "VERB", "INFO", "WARN", "ERRO", "FATA" }; -void LogInit(int modes) { - g_logger.modes = modes; +namespace bfs = boost::filesystem; + +static bfs::path InitLogPath(const std::string& dir) { + if (dir.empty()) { + return bfs::current_path() / WEBCC_LOG_FILE; + } + + bfs::path path = bfs::path(dir); + if (!bfs::exists(path) || !bfs::is_directory(path)) { + boost::system::error_code ec; + if (!bfs::create_directories(path, ec) || ec) { + return bfs::path(); + } + } + + path /= WEBCC_LOG_FILE; + return path; +} + +void LogInit(const std::string& dir, int modes) { + bfs::path path = InitLogPath(dir); + g_logger.reset(new Logger(path.string(), modes)); // Suppose LogInit() is called from the main thread. g_main_thread_id = std::this_thread::get_id(); @@ -74,21 +115,44 @@ static std::string GetThreadID() { void LogWrite(int level, const char* file, int line, const char* format, ...) { assert(format != nullptr); - std::lock_guard lock(g_logger.mutex); - + va_list va_ptr_file; va_list va_ptr_console; + + va_start(va_ptr_file, format); va_start(va_ptr_console, format); - fprintf(stderr, "%s, %s, %5s, %24s, %4d, ", - GetTimestamp().c_str(), kLevelNames[level], GetThreadID().c_str(), - file, line); - vfprintf(stderr, format, va_ptr_console); - fprintf(stderr, "\n"); + if ((g_logger->modes & LOG_FILE) != 0 && g_logger->file != nullptr) { + std::lock_guard lock(g_logger->mutex); + + fprintf(g_logger->file, "%s, %s, %5s, %24s, %4d, ", + GetTimestamp().c_str(), kLevelNames[level], GetThreadID().c_str(), + file, line); + + vfprintf(g_logger->file, format, va_ptr_console); + + fprintf(g_logger->file, "\n"); + + if ((g_logger->modes & LOG_FLUSH) != 0) { + fflush(g_logger->file); + } + } + + if ((g_logger->modes & LOG_CONSOLE) != 0) { + std::lock_guard lock(g_logger->mutex); + + fprintf(stderr, "%s, %s, %5s, %24s, %4d, ", + GetTimestamp().c_str(), kLevelNames[level], GetThreadID().c_str(), + file, line); + + vfprintf(stderr, format, va_ptr_console); + fprintf(stderr, "\n"); - if ((g_logger.modes & FLUSH) == FLUSH) { - fflush(stderr); + if ((g_logger->modes & LOG_FLUSH) != 0) { + fflush(stderr); + } } + va_end(va_ptr_file); va_end(va_ptr_console); } diff --git a/src/webcc/logger.h b/src/webcc/logger.h index a8daad5..e1dee14 100644 --- a/src/webcc/logger.h +++ b/src/webcc/logger.h @@ -6,6 +6,7 @@ #if WEBCC_ENABLE_LOG #include // for strrchr() +#include #define WEBCC_VERB 0 #define WEBCC_INFO 1 @@ -18,13 +19,20 @@ #define WEBCC_LOG_LEVEL WEBCC_WARN #endif +#define WEBCC_LOG_FILE "webcc.log" + namespace webcc { enum LogMode { - FLUSH = 1, + LOG_FILE = 1, // Log to file. + LOG_CONSOLE = 2, // Log to console. + LOG_FLUSH = 4, // Flush on each log. + LOG_OVERWRITE = 8, // Overwrite any existing log file. }; -void LogInit(int modes); +// Initialize logger. +// If |dir| is empty, log file will be generated in current directory. +void LogInit(const std::string& dir, int modes); void LogWrite(int level, const char* file, int line, const char* format, ...); @@ -32,7 +40,7 @@ void LogWrite(int level, const char* file, int line, const char* format, ...); // Initialize the logger with a level. // E.g., LOG_INIT(FLUSH) -#define LOG_INIT(modes) webcc::LogInit(modes); +#define LOG_INIT(dir, modes) webcc::LogInit(dir, modes); #if (defined(WIN32) || defined(_WIN64))