35
35
36
36
using namespace OpenSim ;
37
37
38
+ // Static initialization of default and cout loggers.
39
+ //
40
+ // There are objects from other compilation units that call into the default
41
+ // (and probably cout) logger objects during static initialization so use the
42
+ // "Construct On First Use" idiom to ensure that each logger is allocated and
43
+ // completely initialized before that happens the first time.
44
+
45
+ // Initialize a logger
38
46
static void initializeLogger (spdlog::logger& l, const char * pattern) {
39
47
l.set_level (spdlog::level::info);
48
+ l.flush_on (spdlog::level::info);
40
49
l.set_pattern (pattern);
41
50
}
42
51
43
- // cout logger will be initialized during static initialization time
44
- static std::shared_ptr<spdlog::logger> coutLogger =
45
- spdlog::stdout_color_mt (" cout" );
46
-
47
- // default logger will be initialized during static initialization time
48
- static std::shared_ptr<spdlog::logger> defaultLogger =
49
- spdlog::default_logger ();
50
-
51
- // this function returns a dummy value so that it can be used in an assignment
52
- // expression (below) that *must* be executed in-order at static init time
53
- static bool initializeLogging () {
54
- initializeLogger (*coutLogger, " %v" );
55
- initializeLogger (*defaultLogger, " [%l] %v" );
56
- spdlog::flush_on (spdlog::level::info);
57
- return true ;
52
+ // Return a reference to the static cout logger object, allocating and
53
+ // initializing it on the first call.
54
+ static spdlog::logger& coutLoggerInternal () {
55
+ static std::shared_ptr<spdlog::logger> l = spdlog::stdout_color_mt (" cout" );
56
+ static bool first = true ;
57
+ if (first) {
58
+ initializeLogger (*l, " %v" );
59
+ first = false ;
60
+ }
61
+ return *l;
58
62
}
59
63
60
- // initialization of this variable will have the side-effect of completing the
61
- // initialization of logging
62
- static bool otherStaticInit = initializeLogging();
64
+ // Return a reference to the static default logger object, allocating and
65
+ // initializing it on the first call.
66
+ static spdlog::logger& defaultLoggerInternal () {
67
+ static std::shared_ptr<spdlog::logger> l = spdlog::default_logger ();
68
+ static bool first = true ;
69
+ if (first) {
70
+ initializeLogger (*l, " [%l] %v" );
71
+ first = false ;
72
+ }
73
+ return *l;
74
+ }
63
75
64
76
// the file log sink (e.g. `opensim.log`) is lazily initialized.
65
77
//
@@ -99,30 +111,30 @@ return true;
99
111
// it should perform lazy initialization of the file sink
100
112
spdlog::logger& Logger::getCoutLogger () {
101
113
initFileLoggingAsNeeded ();
102
- return *coutLogger ;
114
+ return coutLoggerInternal () ;
103
115
}
104
116
105
117
// this function is only called when the caller is about to log something, so
106
118
// it should perform lazy initialization of the file sink
107
119
spdlog::logger& Logger::getDefaultLogger () {
108
120
initFileLoggingAsNeeded ();
109
- return *defaultLogger ;
121
+ return defaultLoggerInternal () ;
110
122
}
111
123
112
124
static void addSinkInternal (std::shared_ptr<spdlog::sinks::sink> sink) {
113
- coutLogger-> sinks ().push_back (sink);
114
- defaultLogger-> sinks ().push_back (sink);
125
+ coutLoggerInternal (). sinks ().push_back (sink);
126
+ defaultLoggerInternal (). sinks ().push_back (sink);
115
127
}
116
128
117
129
static void removeSinkInternal (const std::shared_ptr<spdlog::sinks::sink> sink)
118
130
{
119
131
{
120
- auto & sinks = defaultLogger-> sinks ();
132
+ auto & sinks = defaultLoggerInternal (). sinks ();
121
133
auto new_end = std::remove (sinks.begin (), sinks.end (), sink);
122
134
sinks.erase (new_end, sinks.end ());
123
135
}
124
136
{
125
- auto & sinks = coutLogger-> sinks ();
137
+ auto & sinks = coutLoggerInternal (). sinks ();
126
138
auto new_end = std::remove (sinks.begin (), sinks.end (), sink);
127
139
sinks.erase (new_end, sinks.end ());
128
140
}
@@ -158,7 +170,7 @@ void Logger::setLevel(Level level) {
158
170
}
159
171
160
172
Logger::Level Logger::getLevel () {
161
- switch (defaultLogger-> level ()) {
173
+ switch (defaultLoggerInternal (). level ()) {
162
174
case spdlog::level::off: return Level::Off;
163
175
case spdlog::level::critical: return Level::Critical;
164
176
case spdlog::level::err: return Level::Error;
@@ -218,7 +230,7 @@ bool Logger::shouldLog(Level level) {
218
230
default :
219
231
OPENSIM_THROW (Exception, " Internal error." );
220
232
}
221
- return defaultLogger-> should_log (spdlogLevel);
233
+ return defaultLoggerInternal (). should_log (spdlogLevel);
222
234
}
223
235
224
236
void Logger::addFileSink (const std::string& filepath) {
@@ -229,10 +241,11 @@ void Logger::addFileSink(const std::string& filepath) {
229
241
// downstream callers would find it quite surprising if the auto-initializer
230
242
// runs *after* they manually specify a log, so just disable it
231
243
fileSinkAutoInitDisabled = true ;
244
+ spdlog::logger& logger = defaultLoggerInternal ();
232
245
233
246
if (m_filesink) {
234
- defaultLogger-> warn (" Already logging to file '{}'; log file not added. Call "
235
- " removeFileSink() first." , m_filesink->filename ());
247
+ logger. warn (" Already logging to file '{}'; log file not added. Call "
248
+ " removeFileSink() first." , m_filesink->filename ());
236
249
return ;
237
250
}
238
251
@@ -243,9 +256,9 @@ void Logger::addFileSink(const std::string& filepath) {
243
256
std::make_shared<spdlog::sinks::basic_file_sink_mt>(filepath);
244
257
}
245
258
catch (...) {
246
- defaultLogger-> warn (" Can't open file '{}' for writing. Log file will not be created. "
247
- " Check that you have write permissions to the specified path." ,
248
- filepath);
259
+ logger. warn (" Can't open file '{}' for writing. Log file will not be created. "
260
+ " Check that you have write permissions to the specified path." ,
261
+ filepath);
249
262
return ;
250
263
}
251
264
addSinkInternal (m_filesink);
0 commit comments