From 9b7812a0f22f4743ee1618c7d879c8f30004253d Mon Sep 17 00:00:00 2001 From: gabime Date: Sun, 20 Oct 2019 17:40:56 +0300 Subject: [PATCH] auto create log dir --- bench/CMakeLists.txt | 2 - bench/latency.cpp | 22 -------- bench/meson.build | 1 - example/CMakeLists.txt | 2 - example/meson.build | 1 - include/spdlog/details/file_helper-inl.h | 8 +++ include/spdlog/details/os-inl.h | 69 +++++++++++++++++++++++- include/spdlog/details/os.h | 11 ++++ tests/CMakeLists.txt | 4 +- tests/meson.build | 3 -- tests/test_async.cpp | 4 +- tests/test_create_dir.cpp | 27 ++++++++++ tests/test_daily_logger.cpp | 8 +-- tests/test_errors.cpp | 19 +++---- tests/test_file_helper.cpp | 2 +- tests/test_file_logging.cpp | 8 +-- tests/test_macros.cpp | 2 +- tests/utils.cpp | 12 ++--- 18 files changed, 141 insertions(+), 64 deletions(-) create mode 100644 tests/test_create_dir.cpp diff --git a/bench/CMakeLists.txt b/bench/CMakeLists.txt index d087cf66..0ea88423 100644 --- a/bench/CMakeLists.txt +++ b/bench/CMakeLists.txt @@ -24,5 +24,3 @@ target_link_libraries(latency PRIVATE benchmark::benchmark spdlog::spdlog) add_executable(formatter-bench formatter-bench.cpp) target_link_libraries(formatter-bench PRIVATE benchmark::benchmark spdlog::spdlog) - -file(MAKE_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/logs") diff --git a/bench/latency.cpp b/bench/latency.cpp index 730226ca..cd8717d9 100644 --- a/bench/latency.cpp +++ b/bench/latency.cpp @@ -16,26 +16,6 @@ #include "spdlog/sinks/null_sink.h" #include "spdlog/sinks/rotating_file_sink.h" -void prepare_logdir() -{ - spdlog::info("Preparing latency_logs directory.."); -#ifdef _WIN32 - system("if not exist logs mkdir latency_logs"); - system("del /F /Q logs\\*"); -#else - auto rv = system("mkdir -p latency_logs"); - if (rv != 0) - { - throw std::runtime_error("Failed to mkdir -p latency_logs"); - } - rv = system("rm -f latency_logs/*"); - if (rv != 0) - { - throw std::runtime_error("Failed to rm -f latency_logs/*"); - } -#endif -} - void bench_c_string(benchmark::State &state, std::shared_ptr logger) { const char *msg = "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vestibulum pharetra metus cursus " @@ -83,8 +63,6 @@ int main(int argc, char *argv[]) size_t rotating_files = 5; int n_threads = benchmark::CPUInfo::Get().num_cpus; - prepare_logdir(); - // disabled loggers auto disabled_logger = std::make_shared("bench", std::make_shared()); disabled_logger->set_level(spdlog::level::off); diff --git a/bench/meson.build b/bench/meson.build index c2604271..c877b6ac 100644 --- a/bench/meson.build +++ b/bench/meson.build @@ -12,4 +12,3 @@ foreach i : bench_matrix benchmark('bench_' + i[0], bench_exe, args: i[2]) endforeach -run_command(find_program('mkdir'), meson.current_build_dir() + '/logs') diff --git a/example/CMakeLists.txt b/example/CMakeLists.txt index b5fc4060..458ca952 100644 --- a/example/CMakeLists.txt +++ b/example/CMakeLists.txt @@ -25,5 +25,3 @@ if(SPDLOG_BUILD_EXAMPLE_HO) target_link_libraries(example_header_only PRIVATE spdlog::spdlog_header_only) endif() -# Create logs directory -file(MAKE_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/logs") diff --git a/example/meson.build b/example/meson.build index 7e29abb2..c37c4c3c 100644 --- a/example/meson.build +++ b/example/meson.build @@ -1,5 +1,4 @@ executable('example', 'example.cpp', dependencies: spdlog_dep) executable('example_header_only', 'example.cpp', dependencies: spdlog_headeronly_dep) -run_command(find_program('mkdir'), meson.current_build_dir() + '/logs') diff --git a/include/spdlog/details/file_helper-inl.h b/include/spdlog/details/file_helper-inl.h index 440cb290..cd6b5480 100644 --- a/include/spdlog/details/file_helper-inl.h +++ b/include/spdlog/details/file_helper-inl.h @@ -29,9 +29,16 @@ SPDLOG_INLINE void file_helper::open(const filename_t &fname, bool truncate) { close(); auto *mode = truncate ? SPDLOG_FILENAME_T("wb") : SPDLOG_FILENAME_T("ab"); + auto folder_name = os::dir_name(fname); _filename = fname; + for (int tries = 0; tries < open_tries; ++tries) { + if (!folder_name.empty()) + { + os::create_dir(folder_name); + } + if (!os::fopen_s(&fd_, fname, mode)) { return; @@ -129,5 +136,6 @@ SPDLOG_INLINE std::tuple file_helper::split_by_extension // finally - return a valid base and extension tuple return std::make_tuple(fname.substr(0, ext_index), fname.substr(ext_index)); } + } // namespace details } // namespace spdlog diff --git a/include/spdlog/details/os-inl.h b/include/spdlog/details/os-inl.h index f436b0d1..98914eb4 100644 --- a/include/spdlog/details/os-inl.h +++ b/include/spdlog/details/os-inl.h @@ -16,6 +16,7 @@ #include #include #include +#include #include #include #include @@ -196,7 +197,7 @@ SPDLOG_INLINE bool file_exists(const filename_t &filename) SPDLOG_NOEXCEPT #else auto attribs = GetFileAttributesA(filename.c_str()); #endif - return (attribs != INVALID_FILE_ATTRIBUTES && !(attribs & FILE_ATTRIBUTE_DIRECTORY)); + return attribs != INVALID_FILE_ATTRIBUTES; #else // common linux/unix all have the stat system call struct stat buffer; return (::stat(filename.c_str(), &buffer) == 0); @@ -460,6 +461,72 @@ SPDLOG_INLINE void wstr_to_utf8buf(wstring_view_t wstr, memory_buf_t &target) } #endif // (defined(SPDLOG_WCHAR_TO_UTF8_SUPPORT) || defined(SPDLOG_WCHAR_FILENAMES)) && defined(_WIN32) +// return true on success +SPDLOG_INLINE bool mkdir_(const filename_t &path) +{ +#ifdef _WIN32 +#ifdef SPDLOG_WCHAR_FILENAMES + return ::_wmkdir(path.c_str()) == 0; +#else + return ::_mkdir(path.c_str()) == 0; +#endif +#else + return ::mkdir(path.c_str(), mode_t(0755)) == 0; +#endif +} + +// create the given directory - and all directories leading to it +// return true on success +SPDLOG_INLINE bool create_dir(filename_t path) +{ + if (file_exists(path)) + { + return true; + } + using char_type = filename_t::value_type; + std::basic_istringstream istream(path); + filename_t token; + filename_t cur_dir; + char_type sep = '/'; + +#ifdef _WIN32 + // support forward slash in windows + std::replace(path.begin(), path.end(), char_type('\\'), sep); +#endif + while (std::getline(istream, token, sep)) + { + if (!token.empty()) + { + cur_dir += token; + if (!file_exists(cur_dir) && !mkdir_(cur_dir)) + { + return false; + } + } + cur_dir += sep; + } + + return true; +} + +// Return directory name from given path or empty string +// "abc/file" => "abc" +// "abc/" => "abc" +// "abc" => "" +// "abc///" => "abc" +SPDLOG_INLINE filename_t dir_name(filename_t path) +{ + using char_type = filename_t::value_type; + char_type sep = '/'; + +#ifdef _WIN32 + // support forward slash in windows + std::replace(path.begin(), path.end(), char_type('\\'), sep); +#endif + auto pos = path.find_last_of(sep); + return pos != filename_t::npos ? path.substr(0, pos) : filename_t{}; +} + } // namespace os } // namespace details } // namespace spdlog diff --git a/include/spdlog/details/os.h b/include/spdlog/details/os.h index a4a57b7f..0d6abd2f 100644 --- a/include/spdlog/details/os.h +++ b/include/spdlog/details/os.h @@ -89,6 +89,17 @@ bool in_terminal(FILE *file) SPDLOG_NOEXCEPT; void wstr_to_utf8buf(wstring_view_t wstr, memory_buf_t &target); #endif +// Return directory name from given path or empty string +// "abc/file" => "abc" +// "abc/" => "abc" +// "abc" => "" +// "abc///" => "abc" +filename_t dir_name(filename_t path); + +// Create a dir from the given path. +// Return true if succeeded or if this dir already exists. +bool create_dir(filename_t path); + } // namespace os } // namespace details } // namespace spdlog diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 1cf8f805..fd90dd56 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -25,7 +25,8 @@ set(SPDLOG_UTESTS_SOURCES test_fmt_helper.cpp test_stdout_api.cpp test_dup_filter.cpp - test_backtrace.cpp) + test_backtrace.cpp + test_create_dir.cpp) if(NOT SPDLOG_NO_EXCEPTIONS) list(APPEND SPDLOG_UTESTS_SOURCES test_errors.cpp) @@ -35,7 +36,6 @@ if(systemd_FOUND) list(APPEND SPDLOG_UTESTS_SOURCES test_systemd.cpp) endif() -file(MAKE_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/logs") enable_testing() # The compiled library tests diff --git a/tests/meson.build b/tests/meson.build index 73d7b8f8..98b40797 100644 --- a/tests/meson.build +++ b/tests/meson.build @@ -35,7 +35,6 @@ if systemd_dep.found() global_test_deps += systemd_dep endif -run_command('mkdir', 'logs') # -------------------------------------- # --- Build the test executables --- # -------------------------------------- @@ -49,5 +48,3 @@ foreach i : test_matrix test_exe = executable(i[0], test_sources, dependencies: global_test_deps + [i[1]]) test('test_' + i[0], test_exe) endforeach - -run_command(find_program('mkdir'), meson.current_build_dir() + '/logs') \ No newline at end of file diff --git a/tests/test_async.cpp b/tests/test_async.cpp index 6f86cf6c..166ac21e 100644 --- a/tests/test_async.cpp +++ b/tests/test_async.cpp @@ -157,7 +157,7 @@ TEST_CASE("to_file", "[async]") prepare_logdir(); size_t messages = 1024; size_t tp_threads = 1; - std::string filename = "logs/async_test.log"; + std::string filename = "test_logs/async_test.log"; { auto file_sink = std::make_shared(filename, true); auto tp = std::make_shared(messages, tp_threads); @@ -179,7 +179,7 @@ TEST_CASE("to_file multi-workers", "[async]") prepare_logdir(); size_t messages = 1024 * 10; size_t tp_threads = 10; - std::string filename = "logs/async_test.log"; + std::string filename = "test_logs/async_test.log"; { auto file_sink = std::make_shared(filename, true); auto tp = std::make_shared(messages, tp_threads); diff --git a/tests/test_create_dir.cpp b/tests/test_create_dir.cpp new file mode 100644 index 00000000..5e1a2659 --- /dev/null +++ b/tests/test_create_dir.cpp @@ -0,0 +1,27 @@ +/* + * This content is released under the MIT License as specified in https://raw.githubusercontent.com/gabime/spdlog/master/LICENSE + */ +#include "includes.h" + +using spdlog::details::os::create_dir; +using spdlog::details::os::file_exists; + +void test_create_dir(const char *path, const char *normalized_path) +{ + auto rv = create_dir(path); + REQUIRE(rv == true); + REQUIRE(file_exists(normalized_path)); +} + +#include "spdlog/sinks/stdout_color_sinks.h" + +TEST_CASE("create_dir", "[create_dir]") +{ + prepare_logdir(); + test_create_dir("test_logs/dir1/dir1", "test_logs/dir1/dir1"); + test_create_dir("test_logs/dir1///dir2", "test_logs/dir1/dir2"); + test_create_dir("./test_logs/dir1/dir3", "test_logs/dir1/dir3"); + test_create_dir("test_logs/../test_logs/dir1/dir4", "test_logs/dir1/dir4"); + test_create_dir("./test_logs/dir1/dir2/dir99/../dir23", "test_logs/dir1/dir2/dir23"); + spdlog::drop("test-create-dir"); +} diff --git a/tests/test_daily_logger.cpp b/tests/test_daily_logger.cpp index c89ef171..cf2002a3 100644 --- a/tests/test_daily_logger.cpp +++ b/tests/test_daily_logger.cpp @@ -10,7 +10,7 @@ TEST_CASE("daily_logger with dateonly calculator", "[daily_logger]") prepare_logdir(); // calculate filename (time based) - std::string basename = "logs/daily_dateonly"; + std::string basename = "test_logs/daily_dateonly"; std::tm tm = spdlog::details::os::localtime(); spdlog::memory_buf_t w; fmt::format_to(w, "{}_{:04d}-{:02d}-{:02d}", basename, tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday); @@ -44,7 +44,7 @@ TEST_CASE("daily_logger with custom calculator", "[daily_logger]") prepare_logdir(); // calculate filename (time based) - std::string basename = "logs/daily_dateonly"; + std::string basename = "test_logs/daily_dateonly"; std::tm tm = spdlog::details::os::localtime(); spdlog::memory_buf_t w; fmt::format_to(w, "{}{:04d}{:02d}{:02d}", basename, tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday); @@ -119,7 +119,7 @@ static void test_rotate(int days_to_run, uint16_t max_days, uint16_t expected_n_ prepare_logdir(); - std::string basename = "logs/daily_rotate.txt"; + std::string basename = "test_logs/daily_rotate.txt"; daily_file_sink_st sink{basename, 2, 30, true, max_days}; // simulate messages with 24 intervals @@ -130,7 +130,7 @@ static void test_rotate(int days_to_run, uint16_t max_days, uint16_t expected_n_ sink.log(create_msg(offset)); } - REQUIRE(count_files("logs") == static_cast(expected_n_files)); + REQUIRE(count_files("test_logs") == static_cast(expected_n_files)); } TEST_CASE("daily_logger rotate", "[daily_file_sink]") diff --git a/tests/test_errors.cpp b/tests/test_errors.cpp index 65185d3c..4fc40594 100644 --- a/tests/test_errors.cpp +++ b/tests/test_errors.cpp @@ -26,7 +26,7 @@ protected: TEST_CASE("default_error_handler", "[errors]]") { prepare_logdir(); - std::string filename = "logs/simple_log.txt"; + std::string filename = "test_logs/simple_log.txt"; auto logger = spdlog::create("test-error", filename, true); logger->set_pattern("%v"); @@ -43,7 +43,7 @@ struct custom_ex TEST_CASE("custom_error_handler", "[errors]]") { prepare_logdir(); - std::string filename = "logs/simple_log.txt"; + std::string filename = "test_logs/simple_log.txt"; auto logger = spdlog::create("logger", filename, true); logger->flush_on(spdlog::level::info); logger->set_error_handler([=](const std::string &) { throw custom_ex(); }); @@ -75,15 +75,15 @@ TEST_CASE("async_error_handler", "[errors]]") prepare_logdir(); std::string err_msg("log failed with some msg"); - std::string filename = "logs/simple_async_log.txt"; + std::string filename = "test_logs/simple_async_log.txt"; { spdlog::init_thread_pool(128, 1); auto logger = spdlog::create_async("logger", filename, true); logger->set_error_handler([=](const std::string &) { - std::ofstream ofs("logs/custom_err.txt"); + std::ofstream ofs("test_logs/custom_err.txt"); if (!ofs) { - throw std::runtime_error("Failed open logs/custom_err.txt"); + throw std::runtime_error("Failed open test_logs/custom_err.txt"); } ofs << err_msg; }); @@ -94,7 +94,7 @@ TEST_CASE("async_error_handler", "[errors]]") } spdlog::init_thread_pool(128, 1); REQUIRE(count_lines(filename) == 2); - REQUIRE(file_contents("logs/custom_err.txt") == err_msg); + REQUIRE(file_contents("test_logs/custom_err.txt") == err_msg); } // Make sure async error handler is executed @@ -103,12 +103,13 @@ TEST_CASE("async_error_handler2", "[errors]]") prepare_logdir(); std::string err_msg("This is async handler error message"); { + spdlog::details::os::create_dir("test_logs"); spdlog::init_thread_pool(128, 1); auto logger = spdlog::create_async("failed_logger"); logger->set_error_handler([=](const std::string &) { - std::ofstream ofs("logs/custom_err2.txt"); + std::ofstream ofs("test_logs/custom_err2.txt"); if (!ofs) - throw std::runtime_error("Failed open logs/custom_err2.txt"); + throw std::runtime_error("Failed open test_logs/custom_err2.txt"); ofs << err_msg; }); logger->info("Hello failure"); @@ -116,5 +117,5 @@ TEST_CASE("async_error_handler2", "[errors]]") } spdlog::init_thread_pool(128, 1); - REQUIRE(file_contents("logs/custom_err2.txt") == err_msg); + REQUIRE(file_contents("test_logs/custom_err2.txt") == err_msg); } diff --git a/tests/test_file_helper.cpp b/tests/test_file_helper.cpp index 74e3744e..85f62ae2 100644 --- a/tests/test_file_helper.cpp +++ b/tests/test_file_helper.cpp @@ -6,7 +6,7 @@ using spdlog::details::file_helper; using spdlog::details::log_msg; -static const std::string target_filename = "logs/file_helper_test.txt"; +static const std::string target_filename = "test_logs/file_helper_test.txt"; static void write_with_helper(file_helper &helper, size_t howmany) { diff --git a/tests/test_file_logging.cpp b/tests/test_file_logging.cpp index 7563cf65..a8f9b2bb 100644 --- a/tests/test_file_logging.cpp +++ b/tests/test_file_logging.cpp @@ -6,7 +6,7 @@ TEST_CASE("simple_file_logger", "[simple_logger]]") { prepare_logdir(); - std::string filename = "logs/simple_log"; + std::string filename = "test_logs/simple_log"; auto logger = spdlog::create("logger", filename); logger->set_pattern("%v"); @@ -22,7 +22,7 @@ TEST_CASE("simple_file_logger", "[simple_logger]]") TEST_CASE("flush_on", "[flush_on]]") { prepare_logdir(); - std::string filename = "logs/simple_log"; + std::string filename = "test_logs/simple_log"; auto logger = spdlog::create("logger", filename); logger->set_pattern("%v"); @@ -42,7 +42,7 @@ TEST_CASE("rotating_file_logger1", "[rotating_logger]]") { prepare_logdir(); size_t max_size = 1024 * 10; - std::string basename = "logs/rotating_log"; + std::string basename = "test_logs/rotating_log"; auto logger = spdlog::rotating_logger_mt("logger", basename, max_size, 0); for (int i = 0; i < 10; ++i) @@ -59,7 +59,7 @@ TEST_CASE("rotating_file_logger2", "[rotating_logger]]") { prepare_logdir(); size_t max_size = 1024 * 10; - std::string basename = "logs/rotating_log"; + std::string basename = "test_logs/rotating_log"; { // make an initial logger to create the first output file diff --git a/tests/test_macros.cpp b/tests/test_macros.cpp index 26b03cdf..75f53ab2 100644 --- a/tests/test_macros.cpp +++ b/tests/test_macros.cpp @@ -12,7 +12,7 @@ TEST_CASE("debug and trace w/o format string", "[macros]]") { prepare_logdir(); - std::string filename = "logs/simple_log"; + std::string filename = "test_logs/simple_log"; auto logger = spdlog::create("logger", filename); logger->set_pattern("%v"); diff --git a/tests/utils.cpp b/tests/utils.cpp index 6c7f3662..426acb30 100644 --- a/tests/utils.cpp +++ b/tests/utils.cpp @@ -9,18 +9,12 @@ void prepare_logdir() { spdlog::drop_all(); #ifdef _WIN32 - system("if not exist logs mkdir logs"); - system("del /F /Q logs\\*"); + system("rmdir /S /Q test_logs") #else - auto rv = system("mkdir -p logs"); + auto rv = system("rm -rf test_logs"); if (rv != 0) { - throw std::runtime_error("Failed to mkdir -p logs"); - } - rv = system("rm -f logs/*"); - if (rv != 0) - { - throw std::runtime_error("Failed to rm -f logs/*"); + throw std::runtime_error("Failed to rm -rf test_logs"); } #endif }