mirror of
https://github.com/gabime/spdlog.git
synced 2024-12-25 10:01:33 +08:00
Handle file extensions in rotating and daily loggers
This commit is contained in:
parent
60ce47a814
commit
3c4a2bf531
@ -113,12 +113,12 @@ int main(int, char*[])
|
|||||||
my_logger->info("Some log message");
|
my_logger->info("Some log message");
|
||||||
|
|
||||||
// Create a file rotating logger with 5mb size max and 3 rotated files
|
// Create a file rotating logger with 5mb size max and 3 rotated files
|
||||||
auto rotating_logger = spd::rotating_logger_mt("some_logger_name", "logs/mylogfile", 1048576 * 5, 3);
|
auto rotating_logger = spd::rotating_logger_mt("some_logger_name", "logs/mylogfile.txt", 1048576 * 5, 3);
|
||||||
for (int i = 0; i < 10; ++i)
|
for (int i = 0; i < 10; ++i)
|
||||||
rotating_logger->info("{} * {} equals {:>10}", i, i, i*i);
|
rotating_logger->info("{} * {} equals {:>10}", i, i, i*i);
|
||||||
|
|
||||||
// Create a daily logger - a new file is created every day on 2:30am
|
// Create a daily logger - a new file is created every day on 2:30am
|
||||||
auto daily_logger = spd::daily_logger_mt("daily_logger", "logs/daily", 2, 30);
|
auto daily_logger = spd::daily_logger_mt("daily_logger", "logs/daily.txt", 2, 30);
|
||||||
// trigger flush if the log severity is error or higher
|
// trigger flush if the log severity is error or higher
|
||||||
daily_logger->flush_on(spd::level::err);
|
daily_logger->flush_on(spd::level::err);
|
||||||
daily_logger->info(123.44);
|
daily_logger->info(123.44);
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
//
|
//
|
||||||
// Copyright(c) 2015 Gabi Melman.
|
// Copyright(c) 2015 Gabi Melman.
|
||||||
// Distributed under the MIT License (http://opensource.org/licenses/MIT)
|
// Distributed under the MIT License (http://opensource.org/licenses/MIT)
|
||||||
//
|
//
|
||||||
@ -43,16 +43,16 @@ int main(int, char*[])
|
|||||||
|
|
||||||
|
|
||||||
// Create basic file logger (not rotated)
|
// Create basic file logger (not rotated)
|
||||||
auto my_logger = spd::basic_logger_mt("basic_logger", "logs/basic");
|
auto my_logger = spd::basic_logger_mt("basic_logger", "logs/basic-log.txt");
|
||||||
my_logger->info("Some log message");
|
my_logger->info("Some log message");
|
||||||
|
|
||||||
// Create a file rotating logger with 5mb size max and 3 rotated files
|
// Create a file rotating logger with 5mb size max and 3 rotated files
|
||||||
auto rotating_logger = spd::rotating_logger_mt("some_logger_name", "logs/mylogfile", 1048576 * 5, 3);
|
auto rotating_logger = spd::rotating_logger_mt("some_logger_name", "logs/rotating.txt", 1048576 * 5, 3);
|
||||||
for (int i = 0; i < 10; ++i)
|
for (int i = 0; i < 10; ++i)
|
||||||
rotating_logger->info("{} * {} equals {:>10}", i, i, i*i);
|
rotating_logger->info("{} * {} equals {:>10}", i, i, i*i);
|
||||||
|
|
||||||
// Create a daily logger - a new file is created every day on 2:30am
|
// Create a daily logger - a new file is created every day on 2:30am
|
||||||
auto daily_logger = spd::daily_logger_mt("daily_logger", "logs/daily", 2, 30);
|
auto daily_logger = spd::daily_logger_mt("daily_logger", "logs/daily.txt", 2, 30);
|
||||||
// trigger flush if the log severity is error or higher
|
// trigger flush if the log severity is error or higher
|
||||||
daily_logger->flush_on(spd::level::err);
|
daily_logger->flush_on(spd::level::err);
|
||||||
daily_logger->info(123.44);
|
daily_logger->info(123.44);
|
||||||
@ -111,8 +111,7 @@ void async_example()
|
|||||||
{
|
{
|
||||||
size_t q_size = 4096; //queue size must be power of 2
|
size_t q_size = 4096; //queue size must be power of 2
|
||||||
spdlog::set_async_mode(q_size);
|
spdlog::set_async_mode(q_size);
|
||||||
auto async_file = spd::daily_logger_st("async_file_logger", "logs/async_log");
|
auto async_file = spd::daily_logger_st("async_file_logger", "logs/async_log.txt");
|
||||||
|
|
||||||
for (int i = 0; i < 100; ++i)
|
for (int i = 0; i < 100; ++i)
|
||||||
async_file->info("Async message #{}", i);
|
async_file->info("Async message #{}", i);
|
||||||
}
|
}
|
||||||
|
@ -16,6 +16,7 @@
|
|||||||
#include <cstdio>
|
#include <cstdio>
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <thread>
|
#include <thread>
|
||||||
|
#include <tuple>
|
||||||
#include <cerrno>
|
#include <cerrno>
|
||||||
|
|
||||||
namespace spdlog
|
namespace spdlog
|
||||||
@ -84,14 +85,13 @@ public:
|
|||||||
|
|
||||||
void write(const log_msg& msg)
|
void write(const log_msg& msg)
|
||||||
{
|
{
|
||||||
|
|
||||||
size_t msg_size = msg.formatted.size();
|
size_t msg_size = msg.formatted.size();
|
||||||
auto data = msg.formatted.data();
|
auto data = msg.formatted.data();
|
||||||
if (std::fwrite(data, 1, msg_size, _fd) != msg_size)
|
if (std::fwrite(data, 1, msg_size, _fd) != msg_size)
|
||||||
throw spdlog_ex("Failed writing to file " + os::filename_to_str(_filename), errno);
|
throw spdlog_ex("Failed writing to file " + os::filename_to_str(_filename), errno);
|
||||||
}
|
}
|
||||||
|
|
||||||
size_t size()
|
size_t size() const
|
||||||
{
|
{
|
||||||
if (!_fd)
|
if (!_fd)
|
||||||
throw spdlog_ex("Cannot use size() on closed file " + os::filename_to_str(_filename));
|
throw spdlog_ex("Cannot use size() on closed file " + os::filename_to_str(_filename));
|
||||||
@ -103,10 +103,30 @@ public:
|
|||||||
return _filename;
|
return _filename;
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool file_exists(const filename_t& name)
|
static bool file_exists(const filename_t& fname)
|
||||||
{
|
{
|
||||||
|
return os::file_exists(fname);
|
||||||
|
}
|
||||||
|
|
||||||
return os::file_exists(name);
|
//
|
||||||
|
// return basename and extension:
|
||||||
|
//
|
||||||
|
// "mylog.txt" => ("mylog", ".txt")
|
||||||
|
// "mylog" => ("mylog", "")
|
||||||
|
//
|
||||||
|
// the starting dot in filenames is ignored (hidden files):
|
||||||
|
//
|
||||||
|
// "my_folder/.mylog" => ("my_folder/.mylog")
|
||||||
|
// "my_folder/.mylog.txt" => ("my_folder/.mylog", ".txt")
|
||||||
|
|
||||||
|
static std::tuple<filename_t, filename_t> split_by_extenstion(const filename_t& fname)
|
||||||
|
{
|
||||||
|
auto index = fname.rfind('.');
|
||||||
|
bool found_ext = index != filename_t::npos && index !=0 && fname[index - 1] != details::os::folder_sep;
|
||||||
|
if (found_ext)
|
||||||
|
return std::make_tuple(fname.substr(0, index), fname.substr(index));
|
||||||
|
else
|
||||||
|
return std::make_tuple(fname, filename_t());
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
@ -143,6 +143,16 @@ inline bool operator!=(const std::tm& tm1, const std::tm& tm2)
|
|||||||
SPDLOG_CONSTEXPR static const char* eol = SPDLOG_EOL;
|
SPDLOG_CONSTEXPR static const char* eol = SPDLOG_EOL;
|
||||||
SPDLOG_CONSTEXPR static int eol_size = sizeof(SPDLOG_EOL) - 1;
|
SPDLOG_CONSTEXPR static int eol_size = sizeof(SPDLOG_EOL) - 1;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// folder separator
|
||||||
|
#ifdef _WIN32
|
||||||
|
SPDLOG_CONSTEXPR static const char folder_sep = '\\';
|
||||||
|
#else
|
||||||
|
SPDLOG_CONSTEXPR static const char folder_sep = '/';
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
inline void prevent_child_fd(FILE *f)
|
inline void prevent_child_fd(FILE *f)
|
||||||
{
|
{
|
||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
|
@ -77,6 +77,23 @@ public:
|
|||||||
_current_size = _file_helper.size(); //expensive. called only once
|
_current_size = _file_helper.size(); //expensive. called only once
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// calc filename according to index and file extension if exists.
|
||||||
|
// e.g. calc_filename("logs/mylog.txt, 3) => "logs/mylog.3.txt".
|
||||||
|
static filename_t calc_filename(const filename_t& filename, std::size_t index)
|
||||||
|
{
|
||||||
|
std::conditional<std::is_same<filename_t::value_type, char>::value, fmt::MemoryWriter, fmt::WMemoryWriter>::type w;
|
||||||
|
if (index)
|
||||||
|
{
|
||||||
|
filename_t basename, ext;
|
||||||
|
std::tie(basename, ext) = details::file_helper::split_by_extenstion(filename);
|
||||||
|
w.write(SPDLOG_FILENAME_T("{}.{}{}"), basename, index, ext);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
w.write(SPDLOG_FILENAME_T("{}"), filename);
|
||||||
|
}
|
||||||
|
return w.str();
|
||||||
|
}
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
void _sink_it(const details::log_msg& msg) override
|
void _sink_it(const details::log_msg& msg) override
|
||||||
@ -95,23 +112,13 @@ protected:
|
|||||||
_file_helper.flush();
|
_file_helper.flush();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
static filename_t calc_filename(const filename_t& filename, std::size_t index)
|
|
||||||
{
|
|
||||||
std::conditional<std::is_same<filename_t::value_type, char>::value, fmt::MemoryWriter, fmt::WMemoryWriter>::type w;
|
|
||||||
if (index)
|
|
||||||
w.write(SPDLOG_FILENAME_T("{}.{}"), filename, index);
|
|
||||||
else
|
|
||||||
w.write(SPDLOG_FILENAME_T("{}"), filename);
|
|
||||||
return w.str();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Rotate files:
|
// Rotate files:
|
||||||
// log.txt -> log.txt.1
|
// log.txt -> log.1.txt
|
||||||
// log.txt.1 -> log.txt.2
|
// log.1.txt -> log.2.txt
|
||||||
// log.txt.2 -> log.txt.3
|
// log.2.txt -> log.3.txt
|
||||||
// lo3.txt.3 -> delete
|
// log.3.txt -> delete
|
||||||
|
|
||||||
void _rotate()
|
void _rotate()
|
||||||
{
|
{
|
||||||
using details::os::filename_to_str;
|
using details::os::filename_to_str;
|
||||||
@ -150,27 +157,31 @@ typedef rotating_file_sink<details::null_mutex>rotating_file_sink_st;
|
|||||||
*/
|
*/
|
||||||
struct default_daily_file_name_calculator
|
struct default_daily_file_name_calculator
|
||||||
{
|
{
|
||||||
// Create filename for the form basename.YYYY-MM-DD_hh-mm
|
// Create filename for the form filename.YYYY-MM-DD_hh-mm.ext
|
||||||
static filename_t calc_filename(const filename_t& basename)
|
static filename_t calc_filename(const filename_t& filename)
|
||||||
{
|
{
|
||||||
std::tm tm = spdlog::details::os::localtime();
|
std::tm tm = spdlog::details::os::localtime();
|
||||||
|
filename_t basename, ext;
|
||||||
|
std::tie(basename, ext) = details::file_helper::split_by_extenstion(filename);
|
||||||
std::conditional<std::is_same<filename_t::value_type, char>::value, fmt::MemoryWriter, fmt::WMemoryWriter>::type w;
|
std::conditional<std::is_same<filename_t::value_type, char>::value, fmt::MemoryWriter, fmt::WMemoryWriter>::type w;
|
||||||
w.write(SPDLOG_FILENAME_T("{}_{:04d}-{:02d}-{:02d}_{:02d}-{:02d}"), basename, tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday, tm.tm_hour, tm.tm_min);
|
w.write(SPDLOG_FILENAME_T("{}_{:04d}-{:02d}-{:02d}_{:02d}-{:02d}{}"), basename, tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday, tm.tm_hour, tm.tm_min, ext);
|
||||||
return w.str();
|
return w.str();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Generator of daily log file names in format basename.YYYY-MM-DD
|
* Generator of daily log file names in format basename.YYYY-MM-DD.ext
|
||||||
*/
|
*/
|
||||||
struct dateonly_daily_file_name_calculator
|
struct dateonly_daily_file_name_calculator
|
||||||
{
|
{
|
||||||
// Create filename for the form basename.YYYY-MM-DD
|
// Create filename for the form basename.YYYY-MM-DD
|
||||||
static filename_t calc_filename(const filename_t& basename)
|
static filename_t calc_filename(const filename_t& filename)
|
||||||
{
|
{
|
||||||
std::tm tm = spdlog::details::os::localtime();
|
std::tm tm = spdlog::details::os::localtime();
|
||||||
|
filename_t basename, ext;
|
||||||
|
std::tie(basename, ext) = details::file_helper::split_by_extenstion(filename);
|
||||||
std::conditional<std::is_same<filename_t::value_type, char>::value, fmt::MemoryWriter, fmt::WMemoryWriter>::type w;
|
std::conditional<std::is_same<filename_t::value_type, char>::value, fmt::MemoryWriter, fmt::WMemoryWriter>::type w;
|
||||||
w.write(SPDLOG_FILENAME_T("{}_{:04d}-{:02d}-{:02d}"), basename, tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday);
|
w.write(SPDLOG_FILENAME_T("{}_{:04d}-{:02d}-{:02d}{}"), basename, tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday, ext);
|
||||||
return w.str();
|
return w.str();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@ -73,6 +73,77 @@ TEST_CASE("file_helper_reopen2", "[file_helper::reopen(false)]]")
|
|||||||
REQUIRE(helper.size() == expected_size);
|
REQUIRE(helper.size() == expected_size);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST_CASE("file_helper_split_by_extenstion", "[file_helper::split_by_extenstion()]]")
|
||||||
|
{
|
||||||
|
std::string basename, ext;
|
||||||
|
std::tie(basename, ext) = file_helper::split_by_extenstion("mylog.txt");
|
||||||
|
REQUIRE(basename == "mylog");
|
||||||
|
REQUIRE(ext == ".txt");
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE("file_helper_split_by_extenstion2", "[file_helper::split_by_extenstion()]]")
|
||||||
|
{
|
||||||
|
std::string basename, ext;
|
||||||
|
std::tie(basename, ext) = file_helper::split_by_extenstion("mylog");
|
||||||
|
REQUIRE(basename == "mylog");
|
||||||
|
REQUIRE(ext == "");
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE("file_helper_split_by_extenstion3", "[file_helper::split_by_extenstion()]]")
|
||||||
|
{
|
||||||
|
std::string basename, ext;
|
||||||
|
std::tie(basename, ext) = file_helper::split_by_extenstion("mylog.xyz.txt");
|
||||||
|
REQUIRE(basename == "mylog.xyz");
|
||||||
|
REQUIRE(ext == ".txt");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
TEST_CASE("file_helper_split_by_extenstion4", "[file_helper::split_by_extenstion()]]")
|
||||||
|
{
|
||||||
|
std::string basename, ext;
|
||||||
|
std::tie(basename, ext) = file_helper::split_by_extenstion("mylog.xyz....txt");
|
||||||
|
REQUIRE(basename == "mylog.xyz...");
|
||||||
|
REQUIRE(ext == ".txt");
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE("file_helper_split_by_extenstion5", "[file_helper::split_by_extenstion(hidden_file)]]")
|
||||||
|
{
|
||||||
|
std::string basename, ext;
|
||||||
|
std::tie(basename, ext) = file_helper::split_by_extenstion(".mylog");
|
||||||
|
REQUIRE(basename == ".mylog");
|
||||||
|
REQUIRE(ext == "");
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE("file_helper_split_by_extenstion6", "[file_helper::split_by_extenstion(hidden_file)]]")
|
||||||
|
{
|
||||||
|
#ifdef _WIN32
|
||||||
|
auto filename = "folder\\.mylog";
|
||||||
|
auto expected_basename = "folder\\.mylog";
|
||||||
|
#else
|
||||||
|
auto filename = "folder/.mylog";
|
||||||
|
auto expected_basename = "folder/.mylog";
|
||||||
|
#endif
|
||||||
|
std::string basename, ext;
|
||||||
|
std::tie(basename, ext) = file_helper::split_by_extenstion(filename);
|
||||||
|
REQUIRE(basename == expected_basename);
|
||||||
|
REQUIRE(ext == "");
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE("file_helper_split_by_extenstion7", "[file_helper::split_by_extenstion(hidden_file)]]")
|
||||||
|
{
|
||||||
|
#ifdef _WIN32
|
||||||
|
auto filename = "folder\\.mylog.txt";
|
||||||
|
auto expected_basename = "folder\\.mylog";
|
||||||
|
#else
|
||||||
|
auto filename = "folder/.mylog.txt";
|
||||||
|
auto expected_basename = "folder//.mylog";
|
||||||
|
#endif
|
||||||
|
std::string basename, ext;
|
||||||
|
std::tie(basename, ext) = file_helper::split_by_extenstion(filename);
|
||||||
|
REQUIRE(basename == expected_basename);
|
||||||
|
REQUIRE(ext == ".txt");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@ -188,3 +188,55 @@ TEST_CASE("daily_logger with custom calculator", "[daily_logger_custom]]")
|
|||||||
REQUIRE(count_lines(filename) == 10);
|
REQUIRE(count_lines(filename) == 10);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* File name calculations
|
||||||
|
*/
|
||||||
|
|
||||||
|
TEST_CASE("rotating_file_sink::calc_filename1", "[rotating_file_sink]]")
|
||||||
|
{
|
||||||
|
auto filename = spdlog::sinks::rotating_file_sink_st::calc_filename("rotated.txt", 3);
|
||||||
|
REQUIRE(filename == "rotated.3.txt");
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE("rotating_file_sink::calc_filename2", "[rotating_file_sink]]")
|
||||||
|
{
|
||||||
|
auto filename = spdlog::sinks::rotating_file_sink_st::calc_filename("rotated", 3);
|
||||||
|
REQUIRE(filename == "rotated.3");
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE("rotating_file_sink::calc_filename3", "[rotating_file_sink]]")
|
||||||
|
{
|
||||||
|
auto filename = spdlog::sinks::rotating_file_sink_st::calc_filename("rotated.txt", 0);
|
||||||
|
REQUIRE(filename == "rotated.txt");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
TEST_CASE("daily_file_sink::default_daily_file_name_calculator1", "[daily_file_sink]]")
|
||||||
|
{
|
||||||
|
// daily.YYYY-MM-DD_hh-mm.txt
|
||||||
|
auto filename = spdlog::sinks::default_daily_file_name_calculator::calc_filename("daily.txt");
|
||||||
|
std::regex re(R"(^daily_(19|20)\d\d-(0[1-9]|1[012])-(0[1-9]|[12][0-9]|3[01])_\d\d-[0-5][0-9].txt$)");
|
||||||
|
std::smatch match;
|
||||||
|
REQUIRE(std::regex_match(filename, match, re));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE("daily_file_sink::default_daily_file_name_calculator2", "[daily_file_sink]]")
|
||||||
|
{
|
||||||
|
// daily.YYYY-MM-DD_hh-mm.txt
|
||||||
|
auto filename = spdlog::sinks::default_daily_file_name_calculator::calc_filename("daily");
|
||||||
|
std::regex re(R"(^daily_(19|20)\d\d-(0[1-9]|1[012])-(0[1-9]|[12][0-9]|3[01])_\d\d-[0-5][0-9]$)");
|
||||||
|
std::smatch match;
|
||||||
|
REQUIRE(std::regex_match(filename, match, re));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE("daily_file_sink::dateonly_daily_file_name_calculator", "[daily_file_sink]]")
|
||||||
|
{
|
||||||
|
// daily.YYYY-MM-DD_hh-mm.txt
|
||||||
|
auto filename = spdlog::sinks::dateonly_daily_file_name_calculator::calc_filename("daily.txt");
|
||||||
|
// date regex based on https://www.regular-expressions.info/dates.html
|
||||||
|
std::regex re(R"(^daily_(19|20)\d\d-(0[1-9]|1[012])-(0[1-9]|[12][0-9]|3[01])\.txt$)");
|
||||||
|
std::smatch match;
|
||||||
|
REQUIRE(std::regex_match(filename, match, re));
|
||||||
|
}
|
||||||
|
|
||||||
|
@ -6,7 +6,7 @@
|
|||||||
#include <ostream>
|
#include <ostream>
|
||||||
#include <chrono>
|
#include <chrono>
|
||||||
#include <exception>
|
#include <exception>
|
||||||
|
#include <regex>
|
||||||
#include "catch.hpp"
|
#include "catch.hpp"
|
||||||
#include "utils.h"
|
#include "utils.h"
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user