Closed yalov closed 1 month ago
Does the note mean that flush_every() can be used only if all loggers in the application are *****_mt ?
Right.
***_st sink is not thread-safe and is not guaranteed to work.
***_st sink is not thread-safe and is not guaranteed to work.
"not guaranteed to work" mean:
The C++ specification defines that access from multiple threads to a variable that is not thread-safe causes a race condition, resulting to undefined behavior.
See also: https://stackoverflow.com/a/48447270
So, there is several options:
spdlog::flush_every()
std::thread loggers_flush_thread([]() {
auto logger = spdlog::get("logger_mt_that_need_periodic_flush");
while (true) {
std::this_thread::sleep_for(std::chrono::milliseconds(5000));
if (logger)
logger->flush();
else
logger = spdlog::get("logger_mt_that_need_periodic_flush");
}
}).join();
Yes, but both of the two methods you have presented do the same thing.
Yes, but both of the two methods you have presented do the same thing.
I do not understand, for the first one I need to make all logger _mt, even though I need to flush periodically only one of them, but for the second I can make only one logger _mt, and all other leave as *_st, isn't that a difference?
You are correct in your opinion.
It is true that the loggers_flush_thread
thread flushes only logger_mt_that_need_periodic_flush
loggers, so it is not exactly the same behavior as spdlog::flush_every()
, which flushes all loggers.
However, having one thread just to flush one logger is cost prohibitive.
BTW, it is not exactly true that logger_mt is thread-safe. The spdlog logger is always thread-safe.
`spdlog::_logger_mt()and
spdlog::***_logger_st()` are APIs to create a set of logger and sink, the only difference is whether the sink that the logger has is thread-safe (_mt) or not thread-safe (_st).
Thank you, to summarize and with real code samples: I have 1 default logger (sink_mt)
main(){
//....
std::vector<spdlog::sink_ptr> sink_list;
auto sink1 = std::make_shared<spdlog::sinks::rotating_file_sink_mt>("sink1", "file1");
sink_list.push_back(sink);
auto sink2 = std::make_shared<spdlog::sinks::stdout_color_sink_mt>();
sink_list.push_back(stdout_sink);
auto dup_sink = std::make_shared<spdlog::sinks::dup_filter_sink_mt>(std::chrono::milliseconds(2000));
dup_sink->set_sinks(sink_list);
auto logger = std::make_shared<spdlog::logger>("main", dup_sink);
spdlog::set_default_logger(logger);
}
And let say 10 simple file loggers (sink_st), every one writes into one file in a separate thread its own csv thing. Two of them are the most important, so I want to make sure they periodically flush to file. Every logger is a member of a class.
class class1{
std::shared_ptr<spdlog::logger> logger1;
void create(){
auto sink = std::make_shared<spdlog::sinks::rotating_file_sink_st>("sink1", "file1");
logger1 = std::make_shared<spdlog::logger>("logger1", sink);
}
run(){
while(true){
// .......
logger1->info(message);
}
}
}
So, that is the recommended way to make it happen?
First, spdlog::flush_every()
will only flush loggers registered in the spdlog registry.
As far as I can see from the code provided, only logger
is registered in the spdlog registry by spdlog::default_logger()
API.
That is, spdlog::flush_every()
does nothing for rotating_file_sink_st.
Change sink to _mt to at least be able to flush from another thread. If there is a reason why you cannot use _mt, give up interval flushes.
from the wiki:
Does the note mean that
flush_every()
can be used only if all loggers in the application are *****_mt ?What will happened if some of sinks are *_mt and some **_st ?
For example, one of my logger is a file logger, that log csv in one thread, wherefore it uses ****_st sink. How to make it flush, for example, every 5 seconds?