30
30
31
31
using namespace OpenSim ;
32
32
33
- std::shared_ptr<spdlog::logger> Logger::m_cout_logger =
33
+ static void initializeLogger (spdlog::logger& l, const char * pattern) {
34
+ l.set_level (spdlog::level::info);
35
+ l.set_pattern (pattern);
36
+ }
37
+
38
+ // cout logger will be initialized during static initialization time
39
+ static std::shared_ptr<spdlog::logger> coutLogger =
34
40
spdlog::stdout_color_mt (" cout" );
35
- std::shared_ptr<spdlog::sinks::basic_file_sink_mt> Logger::m_filesink = {};
36
- std::shared_ptr<spdlog::logger> Logger::m_default_logger;
37
-
38
- // Force creation of the Logger instane to initialize spdlog::loggers
39
- std::shared_ptr<OpenSim::Logger> Logger::m_osimLogger = Logger::getInstance();
40
-
41
- Logger::Logger () {
42
- m_default_logger = spdlog::default_logger ();
43
- m_default_logger->set_level (spdlog::level::info);
44
- m_default_logger->set_pattern (" [%l] %v" );
45
- m_cout_logger->set_level (spdlog::level::info);
46
- m_cout_logger->set_pattern (" %v" );
47
- // This ensures log files are updated regularly, instead of only when the
48
- // program shuts down.
41
+
42
+ // default logger will be initialized during static initialization time
43
+ static std::shared_ptr<spdlog::logger> defaultLogger =
44
+ spdlog::default_logger ();
45
+
46
+ // this function returns a dummy value so that it can be used in an assignment
47
+ // expression (below) that *must* be executed in-order at static init time
48
+ static bool initializeLogging () {
49
+ initializeLogger (*coutLogger, " %v" );
50
+ initializeLogger (*defaultLogger, " [%l] %v" );
49
51
spdlog::flush_on (spdlog::level::info);
52
+ return true ;
53
+ }
54
+
55
+ // initialization of this variable will have the side-effect of completing the
56
+ // initialization of logging
57
+ static bool otherStaticInit = initializeLogging();
58
+
59
+ // the file log sink (e.g. `opensim.log`) is lazily initialized.
60
+ //
61
+ // it is only initialized when the first log message is about to be written to
62
+ // it. Users *may* disable this functionality before the first log message is
63
+ // written (or disable it statically, by setting OPENSIM_DISABLE_LOG_FILE)
64
+ static std::shared_ptr<spdlog::sinks::basic_file_sink_mt> m_filesink = nullptr ;
65
+
66
+ // if a user manually calls `Logger::(remove|add)FileSink`, auto-initialization
67
+ // should be disabled. Manual usage "overrides" lazy auto-initialization.
68
+ static bool fileSinkAutoInitDisabled = false ;
69
+
70
+ // attempt to auto-initialize the file log, if applicable
71
+ static bool initFileLoggingAsNeeded () {
72
+ #ifdef OPENSIM_DISABLE_LOG_FILE
73
+ // software builders may want to statically ensure that automatic file logging
74
+ // *cannot* happen - even during static initialization. This compiler define
75
+ // outright disables the behavior, which is important in Windows applications
76
+ // that run multiple instances of OpenSim-linked binaries. In Windows, the
77
+ // logs files collide and cause a "multiple processes cannot open the same
78
+ // file" error).
79
+ return true ;
80
+ #else
81
+ static bool initialized = []() {
82
+ if (fileSinkAutoInitDisabled) {
83
+ return true ;
84
+ }
85
+ Logger::addFileSink ();
86
+ return true ;
87
+ }();
88
+
89
+ return initialized;
90
+ #endif
91
+ }
92
+
93
+ // this function is only called when the caller is about to log something, so
94
+ // it should perform lazy initialization of the file sink
95
+ spdlog::logger& Logger::getCoutLogger () {
96
+ initFileLoggingAsNeeded ();
97
+ return *coutLogger;
98
+ }
99
+
100
+ // this function is only called when the caller is about to log something, so
101
+ // it should perform lazy initialization of the file sink
102
+ spdlog::logger& Logger::getDefaultLogger () {
103
+ initFileLoggingAsNeeded ();
104
+ return *defaultLogger;
105
+ }
106
+
107
+ static void addSinkInternal (std::shared_ptr<spdlog::sinks::sink> sink) {
108
+ coutLogger->sinks ().push_back (sink);
109
+ defaultLogger->sinks ().push_back (sink);
110
+ }
111
+
112
+ static void removeSinkInternal (const std::shared_ptr<spdlog::sinks::sink> sink)
113
+ {
114
+ {
115
+ auto & sinks = defaultLogger->sinks ();
116
+ auto new_end = std::remove (sinks.begin (), sinks.end (), sink);
117
+ sinks.erase (new_end, sinks.end ());
118
+ }
119
+ {
120
+ auto & sinks = coutLogger->sinks ();
121
+ auto new_end = std::remove (sinks.begin (), sinks.end (), sink);
122
+ sinks.erase (new_end, sinks.end ());
123
+ }
50
124
}
51
125
52
126
void Logger::setLevel (Level level) {
@@ -79,8 +153,7 @@ void Logger::setLevel(Level level) {
79
153
}
80
154
81
155
Logger::Level Logger::getLevel () {
82
- const auto level = m_default_logger->level ();
83
- switch (level) {
156
+ switch (defaultLogger->level ()) {
84
157
case spdlog::level::off: return Level::Off;
85
158
case spdlog::level::critical: return Level::Critical;
86
159
case spdlog::level::err: return Level::Error;
@@ -140,23 +213,32 @@ bool Logger::shouldLog(Level level) {
140
213
default :
141
214
OPENSIM_THROW (Exception, " Internal error." );
142
215
}
143
- return m_default_logger ->should_log (spdlogLevel);
216
+ return defaultLogger ->should_log (spdlogLevel);
144
217
}
145
218
146
219
void Logger::addFileSink (const std::string& filepath) {
220
+ // this method is either called by the file log auto-initializer, which
221
+ // should now be disabled, or by downstream code trying to manually specify
222
+ // a file sink
223
+ //
224
+ // downstream callers would find it quite surprising if the auto-initializer
225
+ // runs *after* they manually specify a log, so just disable it
226
+ fileSinkAutoInitDisabled = true ;
227
+
147
228
if (m_filesink) {
148
- warn (" Already logging to file '{}'; log file not added. Call "
229
+ defaultLogger-> warn (" Already logging to file '{}'; log file not added. Call "
149
230
" removeFileSink() first." , m_filesink->filename ());
150
231
return ;
151
232
}
233
+
152
234
// check if file can be opened at the specified path if not return meaningful
153
235
// warning rather than bubble the exception up.
154
236
try {
155
237
m_filesink =
156
238
std::make_shared<spdlog::sinks::basic_file_sink_mt>(filepath);
157
239
}
158
240
catch (...) {
159
- warn (" Can't open file '{}' for writing. Log file will not be created. "
241
+ defaultLogger-> warn (" Can't open file '{}' for writing. Log file will not be created. "
160
242
" Check that you have write permissions to the specified path." ,
161
243
filepath);
162
244
return ;
@@ -165,6 +247,19 @@ void Logger::addFileSink(const std::string& filepath) {
165
247
}
166
248
167
249
void Logger::removeFileSink () {
250
+ // if this method is called, then we are probably at a point in the
251
+ // application's lifetime where automatic log allocation is going to cause
252
+ // confusion.
253
+ //
254
+ // callers will be surpised if, after calling this method, auto
255
+ // initialization happens afterwards and the log file still exists - even
256
+ // if they called it to remove some manually-specified log
257
+ fileSinkAutoInitDisabled = true ;
258
+
259
+ if (m_filesink == nullptr ) {
260
+ return ;
261
+ }
262
+
168
263
removeSinkInternal (
169
264
std::static_pointer_cast<spdlog::sinks::sink>(m_filesink));
170
265
m_filesink.reset ();
@@ -178,24 +273,4 @@ void Logger::removeSink(const std::shared_ptr<LogSink> sink) {
178
273
removeSinkInternal (std::static_pointer_cast<spdlog::sinks::sink>(sink));
179
274
}
180
275
181
- void Logger::addSinkInternal (std::shared_ptr<spdlog::sinks::sink> sink) {
182
- m_default_logger->sinks ().push_back (sink);
183
- m_cout_logger->sinks ().push_back (sink);
184
- }
185
-
186
- void Logger::removeSinkInternal (const std::shared_ptr<spdlog::sinks::sink> sink)
187
- {
188
- {
189
- auto & sinks = m_default_logger->sinks ();
190
- auto to_erase = std::find (sinks.cbegin (), sinks.cend (), sink);
191
- if (to_erase != sinks.cend ()) sinks.erase (to_erase);
192
- }
193
- {
194
- auto & sinks = m_cout_logger->sinks ();
195
- auto to_erase = std::find (sinks.cbegin (), sinks.cend (), sink);
196
- if (to_erase != sinks.cend ()) sinks.erase (to_erase);
197
- }
198
- }
199
-
200
-
201
276
0 commit comments