mirror of
https://github.com/gabime/spdlog.git
synced 2025-10-01 19:09:03 +08:00
Use std filesystem (#3284)
* Use std::filesystem for path names and impl
This commit is contained in:
@@ -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
|
||||
|
73
src/details/os_filesystem.cpp
Normal file
73
src/details/os_filesystem.cpp
Normal 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
|
@@ -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{};
|
||||
|
@@ -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)
|
||||
|
@@ -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
|
||||
|
Reference in New Issue
Block a user