mirror of
https://github.com/gabime/spdlog.git
synced 2025-01-14 17:52:05 +08:00
Update step_logger
Test release of a logger step_logger
This commit is contained in:
parent
f17eb1bb56
commit
093edc2dc2
161
include/spdlog/contrib/sinks/step_file_sink.h
Normal file
161
include/spdlog/contrib/sinks/step_file_sink.h
Normal file
@ -0,0 +1,161 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "../../details/file_helper.h"
|
||||||
|
#include "../../details/null_mutex.h"
|
||||||
|
#include "../../fmt/fmt.h"
|
||||||
|
#include "../../sinks/base_sink.h"
|
||||||
|
|
||||||
|
#include <algorithm>
|
||||||
|
#include <cerrno>
|
||||||
|
#include <chrono>
|
||||||
|
#include <cstdio>
|
||||||
|
#include <ctime>
|
||||||
|
#include <mutex>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
// Example for spdlog.h
|
||||||
|
//
|
||||||
|
// Create a file logger which creates new files with a specified time step and fixed file size:
|
||||||
|
//
|
||||||
|
// std::shared_ptr<logger> step_logger_mt(const std::string &logger_name, const filename_t &filename, unsigned seconds = 60, const filename_t &tmp_ext = ".tmp", unsigned max_file_size = std::numeric_limits<unsigned>::max());
|
||||||
|
// std::shared_ptr<logger> step_logger_st(const std::string &logger_name, const filename_t &filename, unsigned seconds = 60, const filename_t &tmp_ext = ".tmp", unsigned max_file_size = std::numeric_limits<unsigned>::max();;
|
||||||
|
|
||||||
|
// Example for spdlog_impl.h
|
||||||
|
// Create a file logger that creates new files with a specified increment
|
||||||
|
// inline std::shared_ptr<spdlog::logger> spdlog::step_logger_mt(
|
||||||
|
// const std::string &logger_name, const filename_t &filename_fmt, unsigned seconds, const filename_t &tmp_ext, unsigned max_file_size)
|
||||||
|
// {
|
||||||
|
// return create<spdlog::sinks::step_file_sink_mt>(logger_name, filename_fmt, seconds, tmp_ext, max_file_size);
|
||||||
|
// }
|
||||||
|
|
||||||
|
// inline std::shared_ptr<spdlog::logger> spdlog::step_logger_st(
|
||||||
|
// const std::string &logger_name, const filename_t &filename_fmt, unsigned seconds, const filename_t &tmp_ext, unsigned max_file_size)
|
||||||
|
// {
|
||||||
|
// return create<spdlog::sinks::step_file_sink_st>(logger_name, filename_fmt, seconds, tmp_ext, max_file_size);
|
||||||
|
// }
|
||||||
|
|
||||||
|
namespace spdlog {
|
||||||
|
namespace sinks {
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Default generator of step log file names.
|
||||||
|
*/
|
||||||
|
struct default_step_file_name_calculator
|
||||||
|
{
|
||||||
|
// Create filename for the form filename_YYYY-MM-DD_hh-mm-ss.ext
|
||||||
|
static std::tuple<filename_t, filename_t> calc_filename(const filename_t &filename, const filename_t &tmp_ext)
|
||||||
|
{
|
||||||
|
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;
|
||||||
|
w.write(SPDLOG_FILENAME_T("{}_{:04d}-{:02d}-{:02d}_{:02d}-{:02d}-{:02d}{}"), basename, tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday,
|
||||||
|
tm.tm_hour, tm.tm_min, tm.tm_sec, tmp_ext);
|
||||||
|
return std::make_tuple(w.str(), ext);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Rotating file sink based on size and a specified time step
|
||||||
|
*/
|
||||||
|
template<class Mutex, class FileNameCalc = default_step_file_name_calculator>
|
||||||
|
class step_file_sink SPDLOG_FINAL : public base_sink<Mutex>
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
step_file_sink(filename_t base_filename, unsigned step_seconds, filename_t tmp_ext, unsigned max_size)
|
||||||
|
: _base_filename(std::move(base_filename))
|
||||||
|
, _tmp_ext(std::move(tmp_ext))
|
||||||
|
, _step_seconds(step_seconds)
|
||||||
|
, _max_size(max_size)
|
||||||
|
{
|
||||||
|
if (step_seconds == 0)
|
||||||
|
{
|
||||||
|
throw spdlog_ex("step_file_sink: Invalid time step in ctor");
|
||||||
|
}
|
||||||
|
if (max_size == 0)
|
||||||
|
{
|
||||||
|
throw spdlog_ex("step_file_sink: Invalid max log size in ctor");
|
||||||
|
}
|
||||||
|
|
||||||
|
_tp = _next_tp();
|
||||||
|
std::tie(_current_filename, _ext) = FileNameCalc::calc_filename(_base_filename, _tmp_ext);
|
||||||
|
|
||||||
|
if (_tmp_ext == _ext)
|
||||||
|
{
|
||||||
|
throw spdlog_ex("step_file_sink: The temporary extension matches the specified in ctor");
|
||||||
|
}
|
||||||
|
|
||||||
|
_file_helper.open(_current_filename);
|
||||||
|
_current_size = _file_helper.size(); // expensive. called only once
|
||||||
|
}
|
||||||
|
|
||||||
|
~step_file_sink()
|
||||||
|
{
|
||||||
|
using details::os::filename_to_str;
|
||||||
|
|
||||||
|
filename_t src =_current_filename, target;
|
||||||
|
std::tie(target, std::ignore) = details::file_helper::split_by_extenstion(src);
|
||||||
|
target += _ext;
|
||||||
|
|
||||||
|
details::os::rename(src, target);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected:
|
||||||
|
void _sink_it(const details::log_msg &msg) override
|
||||||
|
{
|
||||||
|
_current_size += msg.formatted.size();
|
||||||
|
if (std::chrono::system_clock::now() >= _tp || _current_size > _max_size)
|
||||||
|
{
|
||||||
|
close_current_file();
|
||||||
|
|
||||||
|
std::tie(_current_filename, std::ignore) = FileNameCalc::calc_filename(_base_filename, _tmp_ext);
|
||||||
|
_file_helper.open(_current_filename);
|
||||||
|
_tp = _next_tp();
|
||||||
|
_current_size = msg.formatted.size();
|
||||||
|
}
|
||||||
|
_file_helper.write(msg);
|
||||||
|
}
|
||||||
|
|
||||||
|
void _flush() override
|
||||||
|
{
|
||||||
|
_file_helper.flush();
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::chrono::system_clock::time_point _next_tp()
|
||||||
|
{
|
||||||
|
return std::chrono::system_clock::now() + _step_seconds;
|
||||||
|
}
|
||||||
|
|
||||||
|
void close_current_file()
|
||||||
|
{
|
||||||
|
using details::os::filename_to_str;
|
||||||
|
|
||||||
|
filename_t src =_current_filename, target;
|
||||||
|
std::tie(target, std::ignore) = details::file_helper::split_by_extenstion(src);
|
||||||
|
target += _ext;
|
||||||
|
|
||||||
|
if (details::file_helper::file_exists(src) && details::os::rename(src, target) != 0)
|
||||||
|
{
|
||||||
|
throw spdlog_ex("step_file_sink: failed renaming " + filename_to_str(src) + " to " + filename_to_str(target), errno);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const filename_t _base_filename;
|
||||||
|
const filename_t _tmp_ext;
|
||||||
|
const std::chrono::seconds _step_seconds;
|
||||||
|
const unsigned _max_size;
|
||||||
|
|
||||||
|
std::chrono::system_clock::time_point _tp;
|
||||||
|
filename_t _current_filename;
|
||||||
|
filename_t _ext;
|
||||||
|
unsigned _current_size;
|
||||||
|
|
||||||
|
details::file_helper _file_helper;
|
||||||
|
};
|
||||||
|
|
||||||
|
using step_file_sink_mt = step_file_sink<std::mutex>;
|
||||||
|
using step_file_sink_st = step_file_sink<details::null_mutex>;
|
||||||
|
|
||||||
|
} // namespace sinks
|
||||||
|
} // namespace spdlog
|
@ -83,19 +83,6 @@ inline std::shared_ptr<spdlog::logger> spdlog::daily_logger_st(
|
|||||||
return create<spdlog::sinks::daily_file_sink_st>(logger_name, filename, hour, minute);
|
return create<spdlog::sinks::daily_file_sink_st>(logger_name, filename, hour, minute);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create a file logger that creates new files with a specified increment
|
|
||||||
inline std::shared_ptr<spdlog::logger> spdlog::step_logger_mt(
|
|
||||||
const std::string &logger_name, const filename_t &filename_fmt, unsigned seconds, size_t max_file_size)
|
|
||||||
{
|
|
||||||
return create<spdlog::sinks::step_file_sink_mt>(logger_name, filename_fmt, seconds, max_file_size);
|
|
||||||
}
|
|
||||||
|
|
||||||
inline std::shared_ptr<spdlog::logger> spdlog::step_logger_st(
|
|
||||||
const std::string &logger_name, const filename_t &filename_fmt, unsigned seconds, size_t max_file_size)
|
|
||||||
{
|
|
||||||
return create<spdlog::sinks::step_file_sink_st>(logger_name, filename_fmt, seconds, max_file_size);
|
|
||||||
}
|
|
||||||
|
|
||||||
//
|
//
|
||||||
// stdout/stderr loggers
|
// stdout/stderr loggers
|
||||||
//
|
//
|
||||||
|
@ -251,106 +251,5 @@ private:
|
|||||||
using daily_file_sink_mt = daily_file_sink<std::mutex>;
|
using daily_file_sink_mt = daily_file_sink<std::mutex>;
|
||||||
using daily_file_sink_st = daily_file_sink<details::null_mutex>;
|
using daily_file_sink_st = daily_file_sink<details::null_mutex>;
|
||||||
|
|
||||||
/*
|
|
||||||
* Default generator of step log file names.
|
|
||||||
*/
|
|
||||||
struct default_step_file_name_calculator
|
|
||||||
{
|
|
||||||
// Create filename for the form filename_YYYY-MM-DD_hh-mm-ss.ext
|
|
||||||
static std::tuple<filename_t, filename_t> calc_filename(const filename_t &filename)
|
|
||||||
{
|
|
||||||
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;
|
|
||||||
w.write(SPDLOG_FILENAME_T("{}_{:04d}-{:02d}-{:02d}_{:02d}-{:02d}-{:02d}"), basename, tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday,
|
|
||||||
tm.tm_hour, tm.tm_min, tm.tm_sec);
|
|
||||||
return std::make_tuple(w.str(), ext);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Rotating file sink based on size and a specified time step
|
|
||||||
*/
|
|
||||||
template<class Mutex, class FileNameCalc = default_step_file_name_calculator>
|
|
||||||
class step_file_sink SPDLOG_FINAL : public base_sink<Mutex>
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
step_file_sink(filename_t base_filename, unsigned step_seconds, std::size_t max_size)
|
|
||||||
: _base_filename(std::move(base_filename))
|
|
||||||
, _step_seconds(step_seconds)
|
|
||||||
, _max_size(max_size)
|
|
||||||
{
|
|
||||||
_tp = _next_tp();
|
|
||||||
std::tie(_current_filename, _ext) = FileNameCalc::calc_filename(_base_filename);
|
|
||||||
_file_helper.open(_current_filename);
|
|
||||||
_current_size = _file_helper.size(); // expensive. called only once
|
|
||||||
}
|
|
||||||
|
|
||||||
~step_file_sink()
|
|
||||||
{
|
|
||||||
using details::os::filename_to_str;
|
|
||||||
|
|
||||||
filename_t src =_current_filename;
|
|
||||||
filename_t target = _current_filename + _ext;
|
|
||||||
|
|
||||||
details::os::rename(src, target);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected:
|
|
||||||
void _sink_it(const details::log_msg &msg) override
|
|
||||||
{
|
|
||||||
_current_size += msg.formatted.size();
|
|
||||||
if (std::chrono::system_clock::now() >= _tp || _current_size > _max_size)
|
|
||||||
{
|
|
||||||
close_current_file();
|
|
||||||
|
|
||||||
std::tie(_current_filename, std::ignore) = FileNameCalc::calc_filename(_base_filename);
|
|
||||||
_file_helper.open(_current_filename);
|
|
||||||
_tp = _next_tp();
|
|
||||||
_current_size = msg.formatted.size();
|
|
||||||
}
|
|
||||||
_file_helper.write(msg);
|
|
||||||
}
|
|
||||||
|
|
||||||
void _flush() override
|
|
||||||
{
|
|
||||||
_file_helper.flush();
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
|
||||||
std::chrono::system_clock::time_point _next_tp()
|
|
||||||
{
|
|
||||||
return std::chrono::system_clock::now() + _step_seconds;
|
|
||||||
}
|
|
||||||
|
|
||||||
void close_current_file()
|
|
||||||
{
|
|
||||||
using details::os::filename_to_str;
|
|
||||||
|
|
||||||
filename_t src =_current_filename;
|
|
||||||
filename_t target = _current_filename + _ext;
|
|
||||||
|
|
||||||
if (details::file_helper::file_exists(src) && details::os::rename(src, target) != 0)
|
|
||||||
{
|
|
||||||
throw spdlog_ex("step_file_sink: failed renaming " + filename_to_str(src) + " to " + filename_to_str(target), errno);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
filename_t _base_filename;
|
|
||||||
std::chrono::seconds _step_seconds;
|
|
||||||
std::size_t _max_size;
|
|
||||||
|
|
||||||
std::chrono::system_clock::time_point _tp;
|
|
||||||
filename_t _current_filename;
|
|
||||||
filename_t _ext;
|
|
||||||
std::size_t _current_size;
|
|
||||||
|
|
||||||
details::file_helper _file_helper;
|
|
||||||
};
|
|
||||||
|
|
||||||
using step_file_sink_mt = step_file_sink<std::mutex>;
|
|
||||||
using step_file_sink_st = step_file_sink<details::null_mutex>;
|
|
||||||
|
|
||||||
} // namespace sinks
|
} // namespace sinks
|
||||||
} // namespace spdlog
|
} // namespace spdlog
|
||||||
|
@ -91,12 +91,6 @@ std::shared_ptr<logger> rotating_logger_st(
|
|||||||
std::shared_ptr<logger> daily_logger_mt(const std::string &logger_name, const filename_t &filename, int hour = 0, int minute = 0);
|
std::shared_ptr<logger> daily_logger_mt(const std::string &logger_name, const filename_t &filename, int hour = 0, int minute = 0);
|
||||||
std::shared_ptr<logger> daily_logger_st(const std::string &logger_name, const filename_t &filename, int hour = 0, int minute = 0);
|
std::shared_ptr<logger> daily_logger_st(const std::string &logger_name, const filename_t &filename, int hour = 0, int minute = 0);
|
||||||
|
|
||||||
//
|
|
||||||
// Create a file logger which creates new files with a specified time step and fixed file size:
|
|
||||||
//
|
|
||||||
std::shared_ptr<logger> step_logger_mt(const std::string &logger_name, const filename_t &filename, unsigned seconds = 60, size_t max_file_size = std::numeric_limits<int>::max());
|
|
||||||
std::shared_ptr<logger> step_logger_st(const std::string &logger_name, const filename_t &filename, unsigned seconds = 60, size_t max_file_size = std::numeric_limits<int>::max());
|
|
||||||
|
|
||||||
//
|
//
|
||||||
// Create and register stdout/stderr loggers
|
// Create and register stdout/stderr loggers
|
||||||
//
|
//
|
||||||
|
@ -179,71 +179,6 @@ TEST_CASE("daily_logger with custom calculator", "[daily_logger_custom]]")
|
|||||||
REQUIRE(count_lines(filename) == 10);
|
REQUIRE(count_lines(filename) == 10);
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_CASE("step_logger", "[step_logger]]")
|
|
||||||
{
|
|
||||||
prepare_logdir();
|
|
||||||
// calculate filename (time based)
|
|
||||||
std::string basename = "logs/step_log";
|
|
||||||
std::tm tm = spdlog::details::os::localtime();
|
|
||||||
fmt::MemoryWriter w;
|
|
||||||
w.write("{}_{:04d}-{:02d}-{:02d}_{:02d}-{:02d}-{:02d}", basename, tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday, tm.tm_hour, tm.tm_min, tm.tm_sec);
|
|
||||||
|
|
||||||
auto logger = spdlog::step_logger_mt("logger", basename, 60);
|
|
||||||
logger->flush_on(spdlog::level::info);
|
|
||||||
for (int i = 0; i < 10; ++i)
|
|
||||||
{
|
|
||||||
#if !defined(SPDLOG_FMT_PRINTF)
|
|
||||||
logger->info("Test message {}", i);
|
|
||||||
#else
|
|
||||||
logger->info("Test message %d", i);
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
auto filename = w.str();
|
|
||||||
REQUIRE(count_lines(filename) == 10);
|
|
||||||
}
|
|
||||||
|
|
||||||
struct custom_step_file_name_calculator
|
|
||||||
{
|
|
||||||
static std::tuple<spdlog::filename_t, spdlog::filename_t> calc_filename(const spdlog::filename_t &filename)
|
|
||||||
{
|
|
||||||
std::tm tm = spdlog::details::os::localtime();
|
|
||||||
spdlog::filename_t basename, ext;
|
|
||||||
std::tie(basename, ext) = spdlog::details::file_helper::split_by_extenstion(filename);
|
|
||||||
std::conditional<std::is_same<spdlog::filename_t::value_type, char>::value, fmt::MemoryWriter, fmt::WMemoryWriter>::type w;
|
|
||||||
w.write(SPDLOG_FILENAME_T("{}.{:04d}:{:02d}:{:02d}.{:02d}:{:02d}:{:02d}"), basename, tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday,
|
|
||||||
tm.tm_hour, tm.tm_min, tm.tm_sec);
|
|
||||||
return std::make_tuple(w.str(), ext);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
TEST_CASE("step_logger with custom calculator", "[step_logger_custom]]")
|
|
||||||
{
|
|
||||||
using sink_type = spdlog::sinks::step_file_sink<std::mutex, custom_step_file_name_calculator>;
|
|
||||||
|
|
||||||
prepare_logdir();
|
|
||||||
|
|
||||||
std::string basename = "logs/step_log_custom";
|
|
||||||
std::tm tm = spdlog::details::os::localtime();
|
|
||||||
fmt::MemoryWriter w;
|
|
||||||
w.write("{}.{:04d}:{:02d}:{:02d}.{:02d}:{:02d}:{:02d}", basename, tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday,
|
|
||||||
tm.tm_hour, tm.tm_min, tm.tm_sec);
|
|
||||||
|
|
||||||
auto logger = spdlog::create<sink_type>("logger", basename, 60, std::numeric_limits<int>::max());
|
|
||||||
for (int i = 0; i < 10; ++i)
|
|
||||||
{
|
|
||||||
#if !defined(SPDLOG_FMT_PRINTF)
|
|
||||||
logger->info("Test message {}", i);
|
|
||||||
#else
|
|
||||||
logger->info("Test message %d", i);
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
logger->flush();
|
|
||||||
auto filename = w.str();
|
|
||||||
REQUIRE(count_lines(filename) == 10);
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* File name calculations
|
* File name calculations
|
||||||
*/
|
*/
|
||||||
|
Loading…
Reference in New Issue
Block a user