1111#include < fmt/chrono.h>
1212#include < fstream>
1313#include < spdlog/sinks/basic_file_sink.h>
14+ #include < spdlog/sinks/dist_sink.h>
1415#include < spdlog/sinks/stdout_color_sinks.h>
1516#include < spdlog/sinks/stdout_sinks.h>
1617#include < spdlog/spdlog.h>
@@ -36,7 +37,7 @@ bool Logging::mColor = false;
3637std::string Logging::mLastPattern ;
3738std::string Logging::mLastFilenamePattern ;
3839bool Logging::mLogToConsole = true ;
39- #endif
40+ #endif // USE_SPDLOG
4041
4142// Right now this is hard-coded to log messages at least as important as INFO
4243CoutLogger::CoutLogger (LogLevel l) : mShouldLog (l <= Logging::getLogLevel(" " ))
@@ -81,7 +82,68 @@ convert_loglevel(LogLevel level)
8182 }
8283 return slev;
8384}
84- #endif
85+
86+ namespace
87+ {
88+ // Permanent logger to use per each logging partition.
89+ // This is intended to be created on the first use and never destroyed.
90+ struct PermanentLogger
91+ {
92+ std::shared_ptr<spdlog::sinks::dist_sink_mt> mSink ;
93+ LogPtr mLogger ;
94+ };
95+
96+ PermanentLogger
97+ makePermanentLogger (std::string const & name, bool isDefault)
98+ {
99+ auto sink = std::make_shared<spdlog::sinks::dist_sink_mt>();
100+ auto logger = std::make_shared<spdlog::logger>(name, sink);
101+ if (isDefault)
102+ {
103+ // Logging through DEFAULT_LOG before Logging::init() goes to the
104+ // console, matching spdlog's own auto-created default logger.
105+ sink->add_sink (std::make_shared<spdlog::sinks::stdout_color_sink_mt>());
106+ // This also registers the logger in the spdlog registry.
107+ spdlog::set_default_logger (logger);
108+ }
109+ else
110+ {
111+ spdlog::register_logger (logger);
112+ }
113+ return PermanentLogger{std::move (sink), std::move (logger)};
114+ }
115+
116+ PermanentLogger&
117+ defaultPermanentLogger ()
118+ {
119+ static PermanentLogger pl = makePermanentLogger (" default" , true );
120+ return pl;
121+ }
122+
123+ #define LOG_PARTITION (name ) \
124+ PermanentLogger& name##PermanentLogger() \
125+ { \
126+ static PermanentLogger pl = makePermanentLogger (#name, false ); \
127+ return pl; \
128+ }
129+ #include " util/LogPartitions.def"
130+ #undef LOG_PARTITION
131+
132+ // NB: forces creation (and spdlog-registry registration) of every permanent
133+ // logger, so registry-wide operations like spdlog::set_pattern and
134+ // spdlog::set_level cover all of them deterministically.
135+ std::vector<PermanentLogger*>
136+ allPermanentLoggers ()
137+ {
138+ std::vector<PermanentLogger*> loggers;
139+ loggers.push_back (&defaultPermanentLogger ());
140+ #define LOG_PARTITION (name ) loggers.push_back(&name##PermanentLogger());
141+ #include " util/LogPartitions.def"
142+ #undef LOG_PARTITION
143+ return loggers;
144+ }
145+ } // namespace
146+ #endif // USE_SPDLOG
85147
86148void
87149Logging::init (bool truncate)
@@ -166,25 +228,24 @@ Logging::init(bool truncate)
166228 make_shared<basic_file_sink_mt>(filename, /* truncate=*/ false ));
167229 }
168230
169- auto makeLogger =
170- [&](std::string const & name) -> shared_ptr<spdlog::logger> {
171- auto logger =
172- make_shared<spdlog::logger>(name, sinks.begin (), sinks.end ());
173- spdlog::register_logger (logger);
174- return logger;
175- };
176-
177- spdlog::set_default_logger (makeLogger (" default" ));
178- for (auto const & partition : stellar::Logging::kPartitionNames )
231+ // Attach the configured sinks to all the permanent loggers.
232+ for (auto * permanentLogger : allPermanentLoggers ())
179233 {
180- makeLogger (partition );
234+ permanentLogger-> mSink -> set_sinks (sinks );
181235 }
182236 if (mLastPattern .empty ())
183237 {
184238 mLastPattern = " %Y-%m-%dT%H:%M:%S.%e [%^%n %l%$] %v" ;
185239 }
186240 auto maxLevel = mGlobalLogLevel ;
187241 spdlog::set_pattern (mLastPattern );
242+ // NB: these level writes are read lock-free by isLogLevelAtLeast() on
243+ // other threads. set_level() first resets every logger to the global
244+ // level, then the loop applies per-partition overrides, so a thread
245+ // logging concurrently with (re)configuration can momentarily observe a
246+ // partition at the global level (or an override that is about to be
247+ // re-applied). This is benign: reconfiguration is rare, and the worst
248+ // case is a single log line emitted or suppressed at the prior level.
188249 spdlog::set_level (convert_loglevel (mGlobalLogLevel ));
189250 for (auto const & pair : mPartitionLogLevels )
190251 {
@@ -206,10 +267,15 @@ Logging::deinit()
206267 std::lock_guard<std::recursive_mutex> guard (mLogMutex );
207268 if (mInitialized )
208269 {
209- #define LOG_PARTITION (name ) Logging::name##LogPtr = nullptr ;
210- #include " util/LogPartitions.def"
211- #undef LOG_PARTITION
212- spdlog::drop_all ();
270+ // Detach all the sinks from the permanent loggers (which
271+ // closes the log file once the last reference drops).
272+ // The loggers themselves are never destroyed and just stop writing
273+ // anywhere until the next init().
274+ for (auto * permanentLogger : allPermanentLoggers ())
275+ {
276+ permanentLogger->mSink ->flush ();
277+ permanentLogger->mSink ->set_sinks ({});
278+ }
213279 mInitialized = false ;
214280 }
215281#endif
@@ -291,17 +357,9 @@ Logging::setLogLevel(LogLevel level, char const* partition)
291357 mPartitionLogLevels .clear ();
292358 }
293359#if defined(USE_SPDLOG)
360+ // Re-initialize the loggers, which also picks up the new levels.
294361 deinit ();
295362 init ();
296- auto slev = convert_loglevel (level);
297- if (partition)
298- {
299- spdlog::get (partition)->set_level (slev);
300- }
301- else
302- {
303- spdlog::set_level (slev);
304- }
305363#endif
306364}
307365
@@ -384,13 +442,29 @@ Logging::logTrace(std::string const& partition)
384442bool
385443Logging::isLogLevelAtLeast (std::string const & partition, LogLevel level)
386444{
445+ #if defined(USE_SPDLOG)
446+ // Read the (atomic) level of the permanent partition logger instead of
447+ // consulting the level maps under the global mutex: this function is
448+ // called from hot paths on concurrently-running threads. The logger
449+ // levels are kept in sync with the maps by setLogLevel()/init().
450+ auto slev = convert_loglevel (level);
451+ #define LOG_PARTITION (name ) \
452+ if (partition == #name) \
453+ { \
454+ return name##PermanentLogger ().mLogger ->should_log (slev); \
455+ }
456+ #include " util/LogPartitions.def"
457+ #undef LOG_PARTITION
458+ return defaultPermanentLogger ().mLogger ->should_log (slev);
459+ #else
387460 std::lock_guard<std::recursive_mutex> guard (mLogMutex );
388461 auto it = mPartitionLogLevels .find (partition);
389462 if (it != mPartitionLogLevels .end ())
390463 {
391464 return it->second >= level;
392465 }
393466 return mGlobalLogLevel >= level;
467+ #endif
394468}
395469
396470void
@@ -418,27 +492,19 @@ Logging::normalizePartition(std::string const& partition)
418492std::recursive_mutex Logging::mLogMutex ;
419493
420494#if defined(USE_SPDLOG)
421- LogPtr Logging::defaultLogPtr = nullptr ;
422- LogPtr
495+ // These are called by every CLOG_* macro invocation (including ones for
496+ // disabled levels) and must remain lock-free and write-free: they return a
497+ // raw pointer into a permanent logger that is guaranteed to never be destroyed,
498+ // and thus is safe to be stored if necessary.
499+ spdlog::logger*
423500Logging::getDefaultLogPtr ()
424501{
425- std::lock_guard<std::recursive_mutex> guard (mLogMutex );
426- if (!defaultLogPtr)
427- {
428- defaultLogPtr = spdlog::default_logger ();
429- }
430- return defaultLogPtr;
502+ return defaultPermanentLogger ().mLogger .get ();
431503}
432504#define LOG_PARTITION (name ) \
433- LogPtr Logging::name##LogPtr = nullptr ; \
434- LogPtr Logging::get##name##LogPtr() \
505+ spdlog::logger* Logging::get##name##LogPtr() \
435506 { \
436- std::lock_guard<std::recursive_mutex> guard (mLogMutex ); \
437- if (!name##LogPtr) \
438- { \
439- name##LogPtr = spdlog::get (#name); \
440- } \
441- return name##LogPtr; \
507+ return name##PermanentLogger ().mLogger .get (); \
442508 }
443509#include " util/LogPartitions.def"
444510#undef LOG_PARTITION
@@ -458,7 +524,7 @@ Logging::logAtPartitionAndLevel(std::string const& partition, LogLevel level,
458524 }
459525#include " util/LogPartitions.def"
460526#undef LOG_PARTITION
461- LOG_CHECK (spdlog::default_logger (), lev, lg->log (lev, msg));
527+ LOG_CHECK (Logging::getDefaultLogPtr (), lev, lg->log (lev, msg));
462528#else
463529 CoutLogger logger (level) << msg;
464530#endif
0 commit comments