Use std filesystem (#3284)

* Use std::filesystem for path names and impl
This commit is contained in:
Gabi Melman 2024-12-05 19:14:25 +02:00 committed by GitHub
parent daf1b97b8f
commit 08c727e4f8
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
24 changed files with 300 additions and 318 deletions

View File

@ -86,6 +86,7 @@ endif()
option(SPDLOG_PREVENT_CHILD_FD "Prevent from child processes to inherit log file descriptors" OFF)
option(SPDLOG_NO_THREAD_ID "prevent spdlog from querying the thread id on each log call if thread id is not needed" OFF)
option(SPDLOG_DISABLE_DEFAULT_LOGGER "Disable default logger creation" OFF)
option(SPDLOG_NO_TLS "Disable thread local storage" OFF)
# clang-tidy
option(SPDLOG_TIDY "run clang-tidy" OFF)
@ -198,6 +199,7 @@ set(SPDLOG_SRCS
"src/spdlog.cpp"
"src/cfg/helpers.cpp"
"src/details/file_helper.cpp"
"src/details/os_filesystem.cpp"
"src/details/log_msg.cpp"
"src/details/log_msg_buffer.cpp"
"src/details/periodic_worker.cpp"
@ -307,8 +309,13 @@ endif()
# ---------------------------------------------------------------------------------------
# spdlog private defines according to the options
# ---------------------------------------------------------------------------------------
foreach(SPDLOG_OPTION SPDLOG_CLOCK_COARSE SPDLOG_PREVENT_CHILD_FD SPDLOG_NO_THREAD_ID SPDLOG_DISABLE_DEFAULT_LOGGER
SPDLOG_FWRITE_UNLOCKED)
foreach(SPDLOG_OPTION
SPDLOG_CLOCK_COARSE
SPDLOG_PREVENT_CHILD_FD
SPDLOG_NO_THREAD_ID
SPDLOG_DISABLE_DEFAULT_LOGGER
SPDLOG_NO_TLS
SPDLOG_FWRITE_UNLOCKED)
if(${SPDLOG_OPTION})
target_compile_definitions(spdlog PRIVATE ${SPDLOG_OPTION})
endif()

View File

@ -1,4 +1,4 @@
//
//
// Copyright(c) 2015 Gabi Melman.
// Distributed under the MIT License (http://opensource.org/licenses/MIT)
@ -299,16 +299,16 @@ void custom_flags_example() {
void file_events_example() {
// pass the spdlog::file_event_handlers to file sinks for open/close log file notifications
spdlog::file_event_handlers handlers;
handlers.before_open = [](spdlog::filename_t filename) { spdlog::info("Before opening {}", filename); };
handlers.after_open = [](spdlog::filename_t filename, std::FILE *fstream) {
spdlog::info("After opening {}", filename);
handlers.before_open = [](spdlog::filename_t) { spdlog::info("Before opening logfile"); };
handlers.after_open = [](spdlog::filename_t, std::FILE *fstream) {
spdlog::info("After opening logfile");
fputs("After opening\n", fstream);
};
handlers.before_close = [](spdlog::filename_t filename, std::FILE *fstream) {
spdlog::info("Before closing {}", filename);
handlers.before_close = [](spdlog::filename_t, std::FILE *fstream) {
spdlog::info("Before closing logfile");
fputs("Before closing\n", fstream);
};
handlers.after_close = [](spdlog::filename_t filename) { spdlog::info("After closing {}", filename); };
handlers.after_close = [](spdlog::filename_t) { spdlog::info("After closing logfile"); };
auto file_sink = std::make_shared<spdlog::sinks::basic_file_sink_mt>("logs/events-sample.txt", true, handlers);
spdlog::logger my_logger("some_logger", file_sink);
my_logger.info("Some log line");

View File

@ -5,14 +5,14 @@
#include <array>
#include <atomic>
#include <chrono>
#include <cstdio>
#include <exception>
#include <functional>
#include <initializer_list>
#include <memory>
#include <string>
#include <type_traits>
#include <chrono>
#include <string_view>
#include "./source_loc.h"
@ -47,9 +47,6 @@ namespace sinks {
class sink;
}
using filename_t = std::string;
#define SPDLOG_FILENAME_T(s) s
using log_clock = std::chrono::system_clock;
using sink_ptr = std::shared_ptr<sinks::sink>;
using sinks_init_list = std::initializer_list<sink_ptr>;
@ -64,7 +61,6 @@ using wmemory_buf_t = fmt::basic_memory_buffer<wchar_t, 250>;
template <typename... Args>
using format_string_t = fmt::format_string<Args...>;
#define SPDLOG_BUF_TO_STRING(x) fmt::to_string(x)
#define SPDLOG_LEVEL_TRACE 0
#define SPDLOG_LEVEL_DEBUG 1
#define SPDLOG_LEVEL_INFO 2
@ -102,7 +98,7 @@ constexpr std::array<std::string_view, levels_count> short_level_names{"T", "D",
return level_string_views.at(level_to_number(lvl));
}
[[nodiscard]] constexpr const std::string_view to_short_string_view(spdlog::level lvl) noexcept {
[[nodiscard]] constexpr std::string_view to_short_string_view(spdlog::level lvl) noexcept {
return short_level_names.at(level_to_number(lvl));
}
@ -140,33 +136,4 @@ private:
[[noreturn]] SPDLOG_API void throw_spdlog_ex(const std::string &msg, int last_errno);
[[noreturn]] SPDLOG_API void throw_spdlog_ex(std::string msg);
struct file_event_handlers {
file_event_handlers()
: before_open(nullptr),
after_open(nullptr),
before_close(nullptr),
after_close(nullptr) {}
std::function<void(const filename_t &filename)> before_open;
std::function<void(const filename_t &filename, std::FILE *file_stream)> after_open;
std::function<void(const filename_t &filename, std::FILE *file_stream)> before_close;
std::function<void(const filename_t &filename)> after_close;
};
namespace details {
// to_string_view
[[nodiscard]] constexpr spdlog::string_view_t to_string_view(const memory_buf_t &buf) noexcept {
return spdlog::string_view_t{buf.data(), buf.size()};
}
[[nodiscard]] constexpr spdlog::string_view_t to_string_view(spdlog::string_view_t str) noexcept { return str; }
template <typename T, typename... Args>
[[nodiscard]] constexpr fmt::basic_string_view<T> to_string_view(fmt::basic_format_string<T, Args...> fmt) noexcept {
return fmt;
}
} // namespace details
} // namespace spdlog

View File

@ -6,6 +6,7 @@
#include <tuple>
#include "../common.h"
#include "../file_event_handlers.h"
namespace spdlog {
namespace details {
@ -32,21 +33,6 @@ public:
size_t size() const;
const filename_t &filename() const;
//
// return file path and its extension:
//
// "mylog.txt" => ("mylog", ".txt")
// "mylog" => ("mylog", "")
// "mylog." => ("mylog.", "")
// "/dir1/dir2/mylog.txt" => ("/dir1/dir2/mylog", ".txt")
//
// the starting dot in filenames is ignored (hidden files):
//
// ".mylog" => (".mylog". "")
// "my_folder/.mylog" => ("my_folder/.mylog", "")
// "my_folder/.mylog.txt" => ("my_folder/.mylog", ".txt")
static std::tuple<filename_t, filename_t> split_by_extension(const filename_t &fname);
private:
const int open_tries_ = 5;
const unsigned int open_interval_ = 10;

View File

@ -4,8 +4,9 @@
#pragma once
#include <ctime> // std::time_t
#include <tuple>
#include "../common.h"
#include "../filename_t.h"
namespace spdlog {
namespace details {
@ -24,27 +25,13 @@ SPDLOG_API std::tm gmtime() noexcept;
// eol definition and folder separator for the current os
#ifdef _WIN32
constexpr static const char *default_eol = "\r\n";
constexpr static const filename_t::value_type folder_seps_filename[] = SPDLOG_FILENAME_T("\\/");
#else
constexpr static const char *default_eol = "\n";
constexpr static const filename_t::value_type folder_seps_filename[] = SPDLOG_FILENAME_T("/");
#endif
// fopen_s on non windows for writing
SPDLOG_API bool fopen_s(FILE **fp, const filename_t &filename, const filename_t &mode);
// Remove filename. return 0 on success
SPDLOG_API int remove(const filename_t &filename) noexcept;
// Remove file if exists. return 0 on success
// Note: Non atomic (might return failure to delete if concurrently deleted by other process/thread)
SPDLOG_API int remove_if_exists(const filename_t &filename) noexcept;
SPDLOG_API int rename(const filename_t &filename1, const filename_t &filename2) noexcept;
// Return if file exists.
SPDLOG_API bool path_exists(const filename_t &filename) noexcept;
// Return file size according to open FILE* object
SPDLOG_API size_t filesize(FILE *f);
@ -63,8 +50,7 @@ SPDLOG_API size_t thread_id() noexcept;
// See https://github.com/gabime/spdlog/issues/609
SPDLOG_API void sleep_for_millis(unsigned int milliseconds) noexcept;
SPDLOG_API std::string filename_to_str(const filename_t &filename);
// Return pid
SPDLOG_API int pid() noexcept;
// Determine if the terminal supports colors
@ -80,16 +66,6 @@ SPDLOG_API void wstr_to_utf8buf(wstring_view_t wstr, memory_buf_t &target);
SPDLOG_API void utf8_to_wstrbuf(string_view_t str, wmemory_buf_t &target);
#endif
// Return directory name from given path or empty string
// "abc/file" => "abc"
// "abc/" => "abc"
// "abc" => ""
// "abc///" => "abc//"
SPDLOG_API filename_t dir_name(const filename_t &path);
// Create a dir from the given path.
// Return true if succeeded or if this dir already exists.
SPDLOG_API bool create_dir(const filename_t &path);
// non thread safe, cross platform getenv/getenv_s
// return empty string if field not found
@ -103,6 +79,52 @@ SPDLOG_API bool fsync(FILE *fp);
// Return true on success.
SPDLOG_API bool fwrite_bytes(const void *ptr, const size_t n_bytes, FILE *fp);
//
// std::filesystem wrapper functions
//
// Return directory name from given path or empty string
// "abc/file" => "abc"
// "abc/" => "abc"
// "abc" => ""
SPDLOG_API filename_t dir_name(const filename_t &path);
// Create a dir from the given path.
// Return true if succeeded or if this dir already exists.
SPDLOG_API bool create_dir(const filename_t &path);
// Remove filename. return true on success
SPDLOG_API bool remove(const filename_t &filename);
// Remove file if exists. return 0 on success
// Note: Non atomic (might return failure to delete if concurrently deleted by other process/thread)
SPDLOG_API bool remove_if_exists(const filename_t &filename);
// Rename file. return true on success
SPDLOG_API bool rename(const filename_t &filename1, const filename_t &filename2) noexcept;
// Return if file exists.
SPDLOG_API bool path_exists(const filename_t &filename) noexcept;
// Return file path and its extension:
//
// "mylog.txt" => ("mylog", ".txt")
// "mylog" => ("mylog", "")
// "mylog." => ("mylog.", "")
// "/dir1/dir2/mylog.txt" => ("/dir1/dir2/mylog", ".txt")
//
// the starting dot in filenames is ignored (hidden files):
//
// ".mylog" => (".mylog". "")
// "my_folder/.mylog" => ("my_folder/.mylog", "")
// "my_folder/.mylog.txt" => ("my_folder/.mylog", ".txt")
SPDLOG_API std::tuple<filename_t, filename_t> split_by_extension(const filename_t &fname);
// Try tp convert filename to string. Return "??" if failed
SPDLOG_API std::string filename_to_str(const filename_t &filename);
} // namespace os
} // namespace details
} // namespace spdlog

View File

@ -0,0 +1,19 @@
#pragma once
#include <functional>
#include "./filename_t.h"
namespace spdlog {
struct file_event_handlers {
file_event_handlers()
: before_open(nullptr),
after_open(nullptr),
before_close(nullptr),
after_close(nullptr) {}
std::function<void(const filename_t &filename)> before_open;
std::function<void(const filename_t &filename, std::FILE *file_stream)> after_open;
std::function<void(const filename_t &filename, std::FILE *file_stream)> before_close;
std::function<void(const filename_t &filename)> after_close;
};
} // namespace spdlog

View File

@ -0,0 +1,18 @@
// Copyright(c) 2015-present, Gabi Melman & spdlog contributors.
// Distributed under the MIT License (http://opensource.org/licenses/MIT)
#pragma once
#include <filesystem>
#ifdef _WIN32
// In windows, add L prefix for filename literals (e.g. L"filename.txt")
#define SPDLOG_FILENAME_T_INNER(s) L##s
#define SPDLOG_FILENAME_T(s) SPDLOG_FILENAME_T_INNER(s)
#else
#define SPDLOG_FILENAME_T(s) s
#endif
namespace spdlog {
using filename_t = std::filesystem::path;
} // namespace spdlog

View File

@ -6,3 +6,4 @@
#pragma once
#include "fmt/format.h"
#include "fmt/xchar.h"

View File

@ -11,41 +11,43 @@
#include <string>
#include "../common.h"
#include "./base_sink.h"
#include "../details/circular_q.h"
#include "../details/file_helper.h"
#include "../details/null_mutex.h"
#include "../details/os.h"
#include "../details/synchronous_factory.h"
#include "./base_sink.h"
namespace spdlog {
namespace sinks {
/*
* Generator of daily log file names in format basename.YYYY-MM-DD.ext
* Generator of daily log file names in format basename_YYYY-MM-DD.ext
*/
struct daily_filename_calculator {
// Create filename for the form basename.YYYY-MM-DD
struct daily_filename_calculator {
static filename_t calc_filename(const filename_t &filename, const tm &now_tm) {
filename_t basename, ext;
std::tie(basename, ext) = details::file_helper::split_by_extension(filename);
return fmt_lib::format(SPDLOG_FMT_STRING(SPDLOG_FILENAME_T("{}_{:04d}-{:02d}-{:02d}{}")), basename, now_tm.tm_year + 1900,
now_tm.tm_mon + 1, now_tm.tm_mday, ext);
std::tie(basename, ext) = details::os::split_by_extension(filename);
std::basic_ostringstream<filename_t::value_type> oss;
oss << basename.native() << '_' << std::setfill(SPDLOG_FILENAME_T('0')) << std::setw(4) << now_tm.tm_year + 1900 << '-'
<< std::setw(2) << now_tm.tm_mon + 1 << '-' << std::setw(2) << now_tm.tm_mday << ext.native();
return oss.str();
}
};
/*
* Generator of daily log file names with strftime format.
* Usages:
* auto sink =
* std::make_shared<spdlog::sinks::daily_file_format_sink_mt>("myapp-%Y-%m-%d:%H:%M:%S.log", hour,
* minute);" auto logger = spdlog::daily_logger_format_mt("loggername, "myapp-%Y-%m-%d:%X.log",
* hour, minute)"
*
* std::make_shared<spdlog::sinks::daily_file_format_sink_mt>("myapp-%Y-%m-%d:%H:%M:%S.log", hour, minute);
* or
* spdlog::daily_logger_format_mt("loggername, "myapp-%Y-%m-%d:%X.log", hour, minute)"
*
*/
struct daily_filename_format_calculator {
static filename_t calc_filename(const filename_t &file_path, const tm &now_tm) {
std::stringstream stream;
std::basic_ostringstream<filename_t::value_type> stream;
stream << std::put_time(&now_tm, file_path.c_str());
return stream.str();
}
@ -164,7 +166,7 @@ private:
if (filenames_q_.full()) {
auto old_filename = std::move(filenames_q_.front());
filenames_q_.pop_front();
bool ok = remove_if_exists(old_filename) == 0;
bool ok = remove_if_exists(old_filename);
if (!ok) {
filenames_q_.push_back(std::move(current_file));
throw_spdlog_ex("Failed removing daily file " + filename_to_str(old_filename), errno);

View File

@ -22,15 +22,17 @@ namespace spdlog {
namespace sinks {
/*
* Generator of Hourly log file names in format basename.YYYY-MM-DD-HH.ext
* Generator of Hourly log file names in format basename_YYYY-MM-DD_HH.ext
*/
struct hourly_filename_calculator {
// Create filename for the form basename.YYYY-MM-DD-H
static filename_t calc_filename(const filename_t &filename, const tm &now_tm) {
filename_t basename, ext;
std::tie(basename, ext) = details::file_helper::split_by_extension(filename);
return fmt_lib::format(SPDLOG_FILENAME_T("{}_{:04d}-{:02d}-{:02d}_{:02d}{}"), basename, now_tm.tm_year + 1900,
now_tm.tm_mon + 1, now_tm.tm_mday, now_tm.tm_hour, ext);
std::tie(basename, ext) = details::os::split_by_extension(filename);
std::basic_ostringstream<filename_t::value_type> oss;
oss << basename.native() << '_' << std::setfill(SPDLOG_FILENAME_T('0')) << std::setw(4) << now_tm.tm_year + 1900 << '-'
<< std::setw(2) << now_tm.tm_mon + 1 << '-' << std::setw(2) << now_tm.tm_mday << '_' << std::setw(2) << now_tm.tm_hour
<< ext.native();
return oss.str();
}
};

View File

@ -43,7 +43,7 @@ private:
// delete the target if exists, and rename the src file to target
// return true on success, false otherwise.
static bool rename_file_(const filename_t &src_filename, const filename_t &target_filename);
static bool rename_file_(const filename_t &src_filename, const filename_t &target_filename) noexcept;
filename_t base_filename_;
std::size_t max_size_;

View File

@ -1,12 +1,13 @@
// Copyright(c) 2015-present, Gabi Melman & spdlog contributors.
// Distributed under the MIT License (http://opensource.org/licenses/MIT)
#include "spdlog/details/file_helper.h"
#include <cerrno>
#include <cstdio>
#include <utility>
#include <filesystem>
#include "spdlog/details/file_helper.h"
#include "spdlog/common.h"
#include "spdlog/details/os.h"
@ -107,37 +108,6 @@ size_t file_helper::size() const {
const filename_t &file_helper::filename() const { return filename_; }
//
// return file path and its extension:
//
// "mylog.txt" => ("mylog", ".txt")
// "mylog" => ("mylog", "")
// "mylog." => ("mylog.", "")
// "/dir1/dir2/mylog.txt" => ("/dir1/dir2/mylog", ".txt")
//
// the starting dot in filenames is ignored (hidden files):
//
// ".mylog" => (".mylog". "")
// "my_folder/.mylog" => ("my_folder/.mylog", "")
// "my_folder/.mylog.txt" => ("my_folder/.mylog", ".txt")
std::tuple<filename_t, filename_t> file_helper::split_by_extension(const filename_t &fname) {
auto ext_index = fname.rfind('.');
// no valid extension found - return whole path and empty string as
// extension
if (ext_index == filename_t::npos || ext_index == 0 || ext_index == fname.size() - 1) {
return std::make_tuple(fname, filename_t());
}
// treat cases like "/etc/rc.d/somelogfile or "/abc/.hiddenfile"
auto folder_index = fname.find_last_of(details::os::folder_seps_filename);
if (folder_index != filename_t::npos && folder_index >= ext_index - 1) {
return std::make_tuple(fname, filename_t());
}
// 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

View File

@ -0,0 +1,73 @@
// Copyright(c) 2015-present, Gabi Melman & spdlog contributors.
// Distributed under the MIT License (http://opensource.org/licenses/MIT)
#include <filesystem>
#include <string>
#include "spdlog/common.h"
#include "spdlog/details/os.h"
namespace spdlog {
namespace details {
namespace os {
bool remove(const filename_t &filename) {
return std::filesystem::remove(filename);
}
bool remove_if_exists(const filename_t &filename) {
if (path_exists(filename)) {
return os::remove(filename);
}
return false;
}
// Rename if regular file
bool rename(const filename_t &filename1, const filename_t &filename2) noexcept {
if (!std::filesystem::is_regular_file(filename1)) {
return false;
}
std::error_code ec;
std::filesystem::rename(filename1, filename2, ec);
return !ec;
}
// Return true if path exists (file or directory)
bool path_exists(const filename_t &filename) noexcept { return std::filesystem::exists(filename); }
// Return directory name from given path or empty string
// "abc/file" => "abc"
// "abc/" => "abc"
// "abc" => ""
// "abc///" => "abc//"
filename_t dir_name(const filename_t &path) { return path.parent_path(); }
// Create the given directory - and all directories leading to it
// return true on success or if the directory already exists
bool create_dir(const filename_t &path) {
std::error_code ec;
return std::filesystem::create_directories(path, ec) || !ec;
}
// Return file path and its extension:
//
// "mylog.txt" => ("mylog", ".txt")
// "mylog" => ("mylog", "")
// "mylog." => ("mylog", ".")
// "/dir1/dir2/mylog.txt" => ("/dir1/dir2/mylog", ".txt")
//
// the starting dot in filenames is ignored (hidden files):
//
// ".mylog" => (".mylog". "")
// "my_folder/.mylog" => ("my_folder/.mylog", "")
// "my_folder/.mylog.txt" => ("my_folder/.mylog", ".txt")
std::tuple<filename_t, filename_t> split_by_extension(const filename_t &fname) {
const auto ext = fname.extension();
auto without_ext = filename_t(fname).replace_extension();
return std::make_tuple(without_ext, ext);
}
} // namespace os
} // namespace details
} // namespace spdlog

View File

@ -99,19 +99,6 @@ bool fopen_s(FILE **fp, const filename_t &filename, const filename_t &mode) {
return *fp == nullptr;
}
int remove(const filename_t &filename) noexcept { return std::remove(filename.c_str()); }
int remove_if_exists(const filename_t &filename) noexcept { return path_exists(filename) ? remove(filename) : 0; }
int rename(const filename_t &filename1, const filename_t &filename2) noexcept {
return std::rename(filename1.c_str(), filename2.c_str());
}
// Return true if path exists (file or directory)
bool path_exists(const filename_t &filename) noexcept {
struct stat buffer {};
return (::stat(filename.c_str(), &buffer) == 0);
}
// Return file size according to open FILE* object
size_t filesize(FILE *f) {
@ -232,16 +219,22 @@ size_t _thread_id() noexcept {
// Return current thread id as size_t (from thread local storage)
size_t thread_id() noexcept {
// cache thread id in tls
#if defined(SPDLOG_NO_TLS)
return _thread_id();
#else // cache thread id in tls
static thread_local const size_t tid = _thread_id();
return tid;
#endif
}
void sleep_for_millis(unsigned int milliseconds) noexcept {
std::this_thread::sleep_for(std::chrono::milliseconds(milliseconds));
}
std::string filename_to_str(const filename_t &filename) { return filename; }
std::string filename_to_str(const filename_t &filename) {
static_assert(std::is_same_v<filename_t::value_type, char>, "filename_t type must be char");
return filename;
}
int pid() noexcept { return static_cast<int>(::getpid()); }
@ -274,49 +267,6 @@ bool is_color_terminal() noexcept {
// Source: https://github.com/agauniyal/rang/
bool in_terminal(FILE *file) noexcept { return ::isatty(fileno(file)) != 0; }
// return true on success
static bool mkdir_(const filename_t &path) { return ::mkdir(path.c_str(), static_cast<mode_t>(0755)) == 0; }
// create the given directory - and all directories leading to it
// return true on success or if the directory already exists
bool create_dir(const filename_t &path) {
if (path_exists(path)) {
return true;
}
if (path.empty()) {
return false;
}
size_t search_offset = 0;
do {
auto token_pos = path.find_first_of(folder_seps_filename, search_offset);
// treat the entire path as a folder if no folder separator not found
if (token_pos == filename_t::npos) {
token_pos = path.size();
}
auto subdir = path.substr(0, token_pos);
if (!subdir.empty() && !path_exists(subdir) && !mkdir_(subdir)) {
return false; // return error if failed creating dir
}
search_offset = token_pos + 1;
} while (search_offset < path.size());
return true;
}
// Return directory name from given path or empty string
// "abc/file" => "abc"
// "abc/" => "abc"
// "abc" => ""
// "abc///" => "abc//"
filename_t dir_name(const filename_t &path) {
auto pos = path.find_last_of(folder_seps_filename);
return pos != filename_t::npos ? path.substr(0, pos) : filename_t{};
}
std::string getenv(const char *field) {
char *buf = ::getenv(field);
return buf != nullptr ? buf : std::string{};

View File

@ -23,7 +23,6 @@
#include <cstring>
#include <ctime>
#include <string>
#include <thread>
#include "spdlog/common.h"
#include "spdlog/details/os.h"
@ -63,7 +62,7 @@ std::tm gmtime() noexcept {
}
bool fopen_s(FILE **fp, const filename_t &filename, const filename_t &mode) {
*fp = ::_fsopen((filename.c_str()), mode.c_str(), _SH_DENYNO);
*fp = ::_wfsopen((filename.c_str()), mode.c_str(), _SH_DENYNO);
#if defined(SPDLOG_PREVENT_CHILD_FD)
if (*fp != nullptr) {
auto file_handle = reinterpret_cast<HANDLE>(_get_osfhandle(::_fileno(*fp)));
@ -76,19 +75,7 @@ bool fopen_s(FILE **fp, const filename_t &filename, const filename_t &mode) {
return *fp == nullptr;
}
int remove(const filename_t &filename) noexcept { return std::remove(filename.c_str()); }
int remove_if_exists(const filename_t &filename) noexcept { return path_exists(filename) ? remove(filename) : 0; }
int rename(const filename_t &filename1, const filename_t &filename2) noexcept {
return std::rename(filename1.c_str(), filename2.c_str());
}
// Return true if path exists (file or directory)
bool path_exists(const filename_t &filename) noexcept {
struct _stat buffer;
return (::_stat(filename.c_str(), &buffer) == 0);
}
#ifdef _MSC_VER
// avoid warning about unreachable statement at the end of filesize()
@ -149,16 +136,31 @@ size_t _thread_id() noexcept { return static_cast<size_t>(::GetCurrentThreadId()
// Return current thread id as size_t (from thread local storage)
size_t thread_id() noexcept {
// cache thread id in tls
#if defined(SPDLOG_NO_TLS)
return _thread_id();
#else // cache thread id in tls
static thread_local const size_t tid = _thread_id();
return tid;
#endif
}
// This is avoid msvc issue in sleep_for that happens if the clock changes.
// See https://github.com/gabime/spdlog/issues/609
void sleep_for_millis(unsigned int milliseconds) noexcept { ::Sleep(milliseconds); }
std::string filename_to_str(const filename_t &filename) { return filename; }
// Try tp convert wstring filename to string. Return "???" if failed
std::string filename_to_str(const filename_t &filename) {
static_assert(std::is_same_v<filename_t::value_type, wchar_t>, "filename_t type must be wchar_t");
try {
memory_buf_t buf;
wstr_to_utf8buf(filename.wstring(), buf);
return std::string(buf.data(), buf.size());
}
catch (...) {
return "???";
}
}
int pid() noexcept { return static_cast<int>(::GetCurrentProcessId()); }
@ -193,7 +195,7 @@ void wstr_to_utf8buf(wstring_view_t wstr, memory_buf_t &target) {
}
}
throw_spdlog_ex(fmt_lib::format("WideCharToMultiByte failed. Last error: {}", ::GetLastError()));
throw spdlog_ex("WideCharToMultiByte failed");
}
void utf8_to_wstrbuf(string_view_t str, wmemory_buf_t &target) {
@ -222,56 +224,6 @@ void utf8_to_wstrbuf(string_view_t str, wmemory_buf_t &target) {
throw_spdlog_ex(fmt_lib::format("MultiByteToWideChar failed. Last error: {}", ::GetLastError()));
}
// return true on success
static bool mkdir_(const filename_t &path) { return ::_mkdir(path.c_str()) == 0; }
// create the given directory - and all directories leading to it
// return true on success or if the directory already exists
bool create_dir(const filename_t &path) {
if (path_exists(path)) {
return true;
}
if (path.empty()) {
return false;
}
size_t search_offset = 0;
do {
auto token_pos = path.find_first_of(folder_seps_filename, search_offset);
// treat the entire path as a folder if no folder separator not found
if (token_pos == filename_t::npos) {
token_pos = path.size();
}
auto subdir = path.substr(0, token_pos);
// if subdir is just a drive letter, add a slash e.g. "c:"=>"c:\",
// otherwise path_exists(subdir) returns false (issue #3079)
const bool is_drive = subdir.length() == 2 && subdir[1] == ':';
if (is_drive) {
subdir += '\\';
token_pos++;
}
if (!subdir.empty() && !path_exists(subdir) && !mkdir_(subdir)) {
return false; // return error if failed creating dir
}
search_offset = token_pos + 1;
} while (search_offset < path.size());
return true;
}
// Return directory name from given path or empty string
// "abc/file" => "abc"
// "abc/" => "abc"
// "abc" => ""
// "abc///" => "abc//"
filename_t dir_name(const filename_t &path) {
auto pos = path.find_last_of(folder_seps_filename);
return pos != filename_t::npos ? path.substr(0, pos) : filename_t{};
}
std::string getenv(const char *field) {
#if defined(_MSC_VER)

View File

@ -7,11 +7,11 @@
#include <mutex>
#include <string>
#include <tuple>
#include <sstream>
#include "spdlog/common.h"
#include "spdlog/details/file_helper.h"
#include "spdlog/details/os.h"
// #include "spdlog/fmt/fmt.h"
namespace spdlog {
namespace sinks {
@ -51,8 +51,10 @@ filename_t rotating_file_sink<Mutex>::calc_filename(const filename_t &filename,
filename_t basename;
filename_t ext;
std::tie(basename, ext) = details::file_helper::split_by_extension(filename);
return fmt_lib::format(SPDLOG_FMT_STRING(SPDLOG_FILENAME_T("{}.{}{}")), basename, index, ext);
std::tie(basename, ext) = details::os::split_by_extension(filename);
std::basic_ostringstream<filename_t::value_type> oss;
oss << basename.native() << '.' << index << ext.native();
return oss.str();
}
template <typename Mutex>
@ -130,10 +132,8 @@ void rotating_file_sink<Mutex>::rotate_() {
// delete the target if exists, and rename the src file to target
// return true on success, false otherwise.
template <typename Mutex>
bool rotating_file_sink<Mutex>::rename_file_(const filename_t &src_filename, const filename_t &target_filename) {
// try to delete the target file in case it already exists.
(void)details::os::remove(target_filename);
return details::os::rename(src_filename, target_filename) == 0;
bool rotating_file_sink<Mutex>::rename_file_(const filename_t &src_filename, const filename_t &target_filename) noexcept{
return details::os::rename(src_filename, target_filename);
}
} // namespace sinks

View File

@ -31,3 +31,8 @@
#include "spdlog/sinks/null_sink.h"
#include "spdlog/sinks/stdout_color_sinks.h"
#include "spdlog/spdlog.h"
// to_string_view
[[nodiscard]] constexpr spdlog::string_view_t to_string_view(const spdlog::memory_buf_t &buf) noexcept {
return spdlog::string_view_t{buf.data(), buf.size()};
}

View File

@ -43,34 +43,38 @@ TEST_CASE("create_invalid_dir", "[create_dir]") {
}
TEST_CASE("dir_name", "[create_dir]") {
using spdlog::details::os::dir_name;
REQUIRE(dir_name(SPDLOG_FILENAME_T("")).empty());
REQUIRE(dir_name(SPDLOG_FILENAME_T("dir")).empty());
#ifdef WIN32
using spdlog::details::os::dir_name;
#ifdef WIN32
REQUIRE(dir_name(SPDLOG_FILENAME_T(R"(dir\)")) == SPDLOG_FILENAME_T("dir"));
REQUIRE(dir_name(SPDLOG_FILENAME_T(R"(dir\\\)")) == SPDLOG_FILENAME_T(R"(dir\\)"));
REQUIRE(dir_name(SPDLOG_FILENAME_T(R"(dir\\\)")) == SPDLOG_FILENAME_T(R"(dir)"));
REQUIRE(dir_name(SPDLOG_FILENAME_T(R"(dir\file)")) == SPDLOG_FILENAME_T("dir"));
REQUIRE(dir_name(SPDLOG_FILENAME_T(R"(dir/file)")) == SPDLOG_FILENAME_T("dir"));
REQUIRE(dir_name(SPDLOG_FILENAME_T(R"(dir\file.txt)")) == SPDLOG_FILENAME_T("dir"));
REQUIRE(dir_name(SPDLOG_FILENAME_T(R"(dir/file)")) == SPDLOG_FILENAME_T("dir"));
REQUIRE(dir_name(SPDLOG_FILENAME_T(R"(dir\file.txt\)")) == SPDLOG_FILENAME_T(R"(dir\file.txt)"));
REQUIRE(dir_name(SPDLOG_FILENAME_T(R"(\dir\file.txt)")) == SPDLOG_FILENAME_T(R"(\dir)"));
REQUIRE(dir_name(SPDLOG_FILENAME_T(R"(\\dir\file.txt)")) == SPDLOG_FILENAME_T(R"(\\dir)"));
REQUIRE(dir_name(SPDLOG_FILENAME_T(R"(\\dir\file.txt)")) == SPDLOG_FILENAME_T(R"(\\dir\)"));
REQUIRE(dir_name(SPDLOG_FILENAME_T(R"(..\file.txt)")) == SPDLOG_FILENAME_T(".."));
REQUIRE(dir_name(SPDLOG_FILENAME_T(R"(.\file.txt)")) == SPDLOG_FILENAME_T("."));
REQUIRE(dir_name(SPDLOG_FILENAME_T(R"(c:\\a\b\c\d\file.txt)")) == SPDLOG_FILENAME_T(R"(c:\\a\b\c\d)"));
REQUIRE(dir_name(SPDLOG_FILENAME_T(R"(c://a/b/c/d/file.txt)")) == SPDLOG_FILENAME_T(R"(c://a/b/c/d)"));
#endif
REQUIRE(dir_name(SPDLOG_FILENAME_T("")).empty());
REQUIRE(dir_name(SPDLOG_FILENAME_T("dir")).empty());
REQUIRE(dir_name(SPDLOG_FILENAME_T("dir/")) == SPDLOG_FILENAME_T("dir"));
REQUIRE(dir_name(SPDLOG_FILENAME_T("dir///")) == SPDLOG_FILENAME_T("dir//"));
REQUIRE(dir_name(SPDLOG_FILENAME_T("dir///")) == SPDLOG_FILENAME_T("dir"));
REQUIRE(dir_name(SPDLOG_FILENAME_T("dir/file")) == SPDLOG_FILENAME_T("dir"));
REQUIRE(dir_name(SPDLOG_FILENAME_T("dir/file.txt")) == SPDLOG_FILENAME_T("dir"));
REQUIRE(dir_name(SPDLOG_FILENAME_T("dir/file.txt/")) == SPDLOG_FILENAME_T("dir/file.txt"));
REQUIRE(dir_name(SPDLOG_FILENAME_T("/dir/file.txt")) == SPDLOG_FILENAME_T("/dir"));
REQUIRE(dir_name(SPDLOG_FILENAME_T("//dir/file.txt")) == SPDLOG_FILENAME_T("//dir"));
REQUIRE(dir_name(SPDLOG_FILENAME_T("/dir/file.txt")) == SPDLOG_FILENAME_T("/dir"));
REQUIRE(dir_name(SPDLOG_FILENAME_T("../file.txt")) == SPDLOG_FILENAME_T(".."));
REQUIRE(dir_name(SPDLOG_FILENAME_T("./file.txt")) == SPDLOG_FILENAME_T("."));
#ifdef _WIN32
REQUIRE(dir_name(SPDLOG_FILENAME_T("//dir/file.txt")) == SPDLOG_FILENAME_T("//dir/"));
#else
REQUIRE(dir_name(SPDLOG_FILENAME_T("//dir/file.txt")) == SPDLOG_FILENAME_T("//dir"));
#endif
}
#ifdef _WIN32
@ -122,15 +126,4 @@ TEST_CASE("create_abs_path2", "[create_dir]") {
REQUIRE(create_dir(abs_path) == true);
}
TEST_CASE("non_existing_drive", "[create_dir]") {
prepare_logdir();
spdlog::filename_t path;
auto non_existing_drive = find_non_existing_drive();
path += non_existing_drive;
path += SPDLOG_FILENAME_T(":\\");
REQUIRE(create_dir(path) == false);
path += SPDLOG_FILENAME_T("subdir");
REQUIRE(create_dir(path) == false);
}
#endif // _WIN32

View File

@ -5,6 +5,7 @@
#include "includes.h"
#include "spdlog/sinks/daily_file_sink.h"
#include "spdlog/sinks/rotating_file_sink.h"
#include "spdlog/sinks/hourly_file_sink.h"
using filename_memory_buf_t = spdlog::memory_buf_t;
@ -16,9 +17,8 @@ TEST_CASE("daily_logger with dateonly calculator", "[daily_logger]") {
// calculate filename (time based)
spdlog::filename_t basename = SPDLOG_FILENAME_T("test_logs/daily_dateonly");
std::tm tm = spdlog::details::os::localtime();
filename_memory_buf_t w;
spdlog::fmt_lib::format_to(std::back_inserter(w), SPDLOG_FILENAME_T("{}_{:04d}-{:02d}-{:02d}"), basename, tm.tm_year + 1900,
tm.tm_mon + 1, tm.tm_mday);
auto w = spdlog::fmt_lib::format(SPDLOG_FILENAME_T("{}_{:04d}-{:02d}-{:02d}"), basename.native(), tm.tm_year + 1900,
tm.tm_mon + 1, tm.tm_mday);
auto logger = spdlog::create<sink_type>("logger", basename, 0, 0);
for (int i = 0; i < 10; ++i) {
@ -26,16 +26,15 @@ TEST_CASE("daily_logger with dateonly calculator", "[daily_logger]") {
}
logger->flush();
require_message_count(SPDLOG_BUF_TO_STRING(w), 10);
require_message_count(w, 10);
}
struct custom_daily_file_name_calculator {
static spdlog::filename_t calc_filename(const spdlog::filename_t &basename, const tm &now_tm) {
filename_memory_buf_t w;
spdlog::fmt_lib::format_to(std::back_inserter(w), SPDLOG_FILENAME_T("{}{:04d}{:02d}{:02d}"), basename,
now_tm.tm_year + 1900, now_tm.tm_mon + 1, now_tm.tm_mday);
auto w = spdlog::fmt_lib::format(SPDLOG_FILENAME_T("{}{:04d}{:02d}{:02d}"), basename.native(), now_tm.tm_year + 1900,
now_tm.tm_mon + 1, now_tm.tm_mday);
return SPDLOG_BUF_TO_STRING(w);
return w;
}
};
@ -47,9 +46,10 @@ TEST_CASE("daily_logger with custom calculator", "[daily_logger]") {
// calculate filename (time based)
spdlog::filename_t basename = SPDLOG_FILENAME_T("test_logs/daily_dateonly");
std::tm tm = spdlog::details::os::localtime();
filename_memory_buf_t w;
spdlog::fmt_lib::format_to(std::back_inserter(w), SPDLOG_FILENAME_T("{}{:04d}{:02d}{:02d}"), basename, tm.tm_year + 1900,
tm.tm_mon + 1, tm.tm_mday);
auto w = spdlog::fmt_lib::format(SPDLOG_FILENAME_T("{}{:04d}{:02d}{:02d}"), basename.native(), tm.tm_year + 1900,
tm.tm_mon + 1,
tm.tm_mday);
auto logger = spdlog::create<sink_type>("logger", basename, 0, 0);
for (int i = 0; i < 10; ++i) {
@ -57,7 +57,7 @@ TEST_CASE("daily_logger with custom calculator", "[daily_logger]") {
}
logger->flush();
require_message_count(SPDLOG_BUF_TO_STRING(w), 10);
require_message_count(w, 10);
}
/*
@ -82,17 +82,32 @@ TEST_CASE("rotating_file_sink::calc_filename3", "[rotating_file_sink]") {
// regex supported only from gcc 4.9 and above
#if defined(_MSC_VER) || !(__GNUC__ <= 4 && __GNUC_MINOR__ < 9)
#include <regex>
#include <regex>
TEST_CASE("daily_file_sink::daily_filename_calculator", "[daily_file_sink]") {
// daily_YYYY-MM-DD_hh-mm.txt
auto filename =
spdlog::sinks::daily_filename_calculator::calc_filename(SPDLOG_FILENAME_T("daily.txt"),
spdlog::details::os::localtime());
// date regex based on https://www.regular-expressions.info/dates.html
std::basic_regex<spdlog::filename_t::value_type> re(
SPDLOG_FILENAME_T(R"(^daily_(19|20)\d\d-(0[1-9]|1[012])-(0[1-9]|[12][0-9]|3[01])\.txt$)"));
std::match_results<spdlog::filename_t::string_type::const_iterator> match;
REQUIRE(std::regex_match(filename.native(), match, re));
}
TEST_CASE("hourly_file_sink::hourly_filename_calculator", "[hrouly_file_sink]") {
// daily_YYYY-MM-DD_hh-mm.txt
auto filename =
spdlog::sinks::daily_filename_calculator::calc_filename(SPDLOG_FILENAME_T("daily.txt"), spdlog::details::os::localtime());
spdlog::sinks::hourly_filename_calculator::calc_filename(SPDLOG_FILENAME_T("hourly.txt"), spdlog::details::os::localtime());
// date regex based on https://www.regular-expressions.info/dates.html
std::basic_regex<spdlog::filename_t::value_type> re(
SPDLOG_FILENAME_T(R"(^daily_(19|20)\d\d-(0[1-9]|1[012])-(0[1-9]|[12][0-9]|3[01])\.txt$)"));
std::match_results<spdlog::filename_t::const_iterator> match;
REQUIRE(std::regex_match(filename, match, re));
SPDLOG_FILENAME_T(R"(^hourly_(19|20)\d\d-(0[1-9]|1[012])-(0[1-9]|[12][0-9]|3[01])_\d\d\.txt$)"));
std::match_results<spdlog::filename_t::string_type::const_iterator> match;
REQUIRE(std::regex_match(filename.native(), match, re));
}
#endif
@ -148,4 +163,4 @@ TEST_CASE("daily_logger rotate", "[daily_file_sink]") {
test_rotate(days_to_run, 10, 10);
test_rotate(days_to_run, 11, 10);
test_rotate(days_to_run, 20, 10);
}
}

View File

@ -70,7 +70,7 @@ static void test_split_ext(const spdlog::filename_t::value_type *fname,
spdlog::filename_t basename;
spdlog::filename_t ext;
std::tie(basename, ext) = file_helper::split_by_extension(filename);
std::tie(basename, ext) = spdlog::details::os::split_by_extension(filename);
REQUIRE(basename == expected_base);
REQUIRE(ext == expected_ext);
}
@ -82,7 +82,7 @@ TEST_CASE("file_helper_split_by_extension", "[file_helper::split_by_extension()]
test_split_ext(SPDLOG_FILENAME_T("/aaa/bb.d/mylog"), SPDLOG_FILENAME_T("/aaa/bb.d/mylog"), SPDLOG_FILENAME_T(""));
test_split_ext(SPDLOG_FILENAME_T("/aaa/bb.d/mylog.txt"), SPDLOG_FILENAME_T("/aaa/bb.d/mylog"), SPDLOG_FILENAME_T(".txt"));
test_split_ext(SPDLOG_FILENAME_T("aaa/bbb/ccc/mylog.txt"), SPDLOG_FILENAME_T("aaa/bbb/ccc/mylog"), SPDLOG_FILENAME_T(".txt"));
test_split_ext(SPDLOG_FILENAME_T("aaa/bbb/ccc/mylog."), SPDLOG_FILENAME_T("aaa/bbb/ccc/mylog."), SPDLOG_FILENAME_T(""));
test_split_ext(SPDLOG_FILENAME_T("aaa/bbb/ccc/mylog."), SPDLOG_FILENAME_T("aaa/bbb/ccc/mylog"), SPDLOG_FILENAME_T("."));
test_split_ext(SPDLOG_FILENAME_T("aaa/bbb/ccc/.mylog.txt"), SPDLOG_FILENAME_T("aaa/bbb/ccc/.mylog"),
SPDLOG_FILENAME_T(".txt"));
test_split_ext(SPDLOG_FILENAME_T("/aaa/bbb/ccc/mylog.txt"), SPDLOG_FILENAME_T("/aaa/bbb/ccc/mylog"),
@ -92,7 +92,7 @@ TEST_CASE("file_helper_split_by_extension", "[file_helper::split_by_extension()]
test_split_ext(SPDLOG_FILENAME_T(".././mylog.txt"), SPDLOG_FILENAME_T(".././mylog"), SPDLOG_FILENAME_T(".txt"));
test_split_ext(SPDLOG_FILENAME_T(".././mylog.txt/xxx"), SPDLOG_FILENAME_T(".././mylog.txt/xxx"), SPDLOG_FILENAME_T(""));
test_split_ext(SPDLOG_FILENAME_T("/mylog.txt"), SPDLOG_FILENAME_T("/mylog"), SPDLOG_FILENAME_T(".txt"));
test_split_ext(SPDLOG_FILENAME_T("//mylog.txt"), SPDLOG_FILENAME_T("//mylog"), SPDLOG_FILENAME_T(".txt"));
test_split_ext(SPDLOG_FILENAME_T("///mylog.txt"), SPDLOG_FILENAME_T("///mylog"), SPDLOG_FILENAME_T(".txt"));
test_split_ext(SPDLOG_FILENAME_T(""), SPDLOG_FILENAME_T(""), SPDLOG_FILENAME_T(""));
test_split_ext(SPDLOG_FILENAME_T("."), SPDLOG_FILENAME_T("."), SPDLOG_FILENAME_T(""));
test_split_ext(SPDLOG_FILENAME_T("..txt"), SPDLOG_FILENAME_T("."), SPDLOG_FILENAME_T(".txt"));

View File

@ -2,7 +2,6 @@
#include "includes.h"
using spdlog::memory_buf_t;
using spdlog::details::to_string_view;
void test_pad2(int n, const char *expected) {
memory_buf_t buf;

View File

@ -3,7 +3,6 @@
#include "test_sink.h"
using spdlog::memory_buf_t;
using spdlog::details::to_string_view;
// log to str and return it
template <typename... Args>

View File

@ -27,7 +27,7 @@ std::string file_contents(const std::string &filename) {
return std::string((std::istreambuf_iterator<char>(ifs)), (std::istreambuf_iterator<char>()));
}
std::size_t count_lines(const std::string &filename) {
std::size_t count_lines(const spdlog::filename_t &filename) {
std::ifstream ifs(filename);
if (!ifs) {
throw std::runtime_error("Failed open file ");
@ -39,7 +39,7 @@ std::size_t count_lines(const std::string &filename) {
return counter;
}
void require_message_count(const std::string &filename, const std::size_t messages) {
void require_message_count(const std::filesystem::path &filename, const std::size_t messages) {
if (strlen(spdlog::details::os::default_eol) == 0) {
REQUIRE(count_lines(filename) == 1);
} else {

View File

@ -2,6 +2,7 @@
#include <cstddef>
#include <string>
#include <filesystem>
std::size_t count_files(const std::string &folder);
@ -9,9 +10,10 @@ void prepare_logdir();
std::string file_contents(const std::string &filename);
std::size_t count_lines(const std::string &filename);
//std::size_t count_lines(const std::string &filename);
std::size_t count_lines(const std::filesystem::path &filename);
void require_message_count(const std::string &filename, const std::size_t messages);
void require_message_count(const std::filesystem::path &filename, const std::size_t messages);
std::size_t get_filesize(const std::string &filename);