1 // -*- C++ -*-
2 /**
3 * @brief Log records all user information to a file or tty
4 *
5 * Copyright 2005-2021 Airbus-EDF-IMACS-ONERA-Phimeca
6 *
7 * This library is free software: you can redistribute it and/or modify
8 * it under the terms of the GNU Lesser General Public License as published by
9 * the Free Software Foundation, either version 3 of the License, or
10 * (at your option) any later version.
11 *
12 * This library is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU Lesser General Public License for more details.
16 *
17 * You should have received a copy of the GNU Lesser General Public License
18 * along with this library. If not, see <http://www.gnu.org/licenses/>.
19 *
20 */
21 #include <iostream>
22 #include <cstdlib>
23 #include <cassert>
24 #include <errno.h>
25
26 #include "openturns/OTthread.hxx"
27 #include "openturns/Log.hxx"
28 #include "openturns/OTconfig.hxx"
29 #include "openturns/MutexLock.hxx"
30
31 BEGIN_NAMESPACE_OPENTURNS
32
33 static pthread_mutex_t Log_InstanceMutex_;
34 static Log * Log_P_instance_ = 0;
35 static const Log_init static_initializer_Log;
36
37
38 static inline
make_prefix(const _Prefix::Value & color,const _Prefix::Value & nocolor,const _Prefix::Value & prefix)39 _Prefix make_prefix( const _Prefix::Value & color, const _Prefix::Value & nocolor, const _Prefix::Value & prefix)
40 {
41 return _Prefix( color, nocolor, prefix );
42 }
43
operator <<(std::ostream & os,const _Prefix & pfx)44 std::ostream & operator << ( std::ostream & os, const _Prefix & pfx )
45 {
46 return os << (TTY::ColoredOutput() ? pfx.color_ : pfx.nocolor_) << pfx.prefix_;
47 }
48
49
Log_init()50 Log_init::Log_init()
51 {
52 if (!Log_P_instance_)
53 {
54 #ifndef OT_MUTEXINIT_NOCHECK
55 pthread_mutexattr_t attr;
56 pthread_mutexattr_init( &attr );
57 pthread_mutexattr_settype( &attr, PTHREAD_MUTEX_RECURSIVE );
58 pthread_mutex_init( &Log_InstanceMutex_, &attr );
59 #else
60 pthread_mutex_init( &Log_InstanceMutex_, NULL );
61 #endif
62 Log_P_instance_ = new Log;
63 Log_P_instance_->push(Log::Entry(Log::INFO, "*** Log Beginning ***"));
64 }
65 assert(Log_P_instance_);
66 }
67
~Log_init()68 Log_init::~Log_init()
69 {
70 if (Log_P_instance_)
71 {
72 Log_P_instance_->push(Log::Entry(Log::INFO, "*** Log End ***"));
73 pthread_mutex_destroy(&Log_InstanceMutex_);
74 }
75 delete Log_P_instance_;
76 Log_P_instance_ = 0;
77 }
78
79
80 const Log::Severity Log::NONE = 0;
81 const Log::Severity Log::ALL = ~0;
82
83 const Log::Severity Log::DBG = 1 << 0;
84 const Log::Severity Log::INFO = 1 << 2;
85 const Log::Severity Log::USER = 1 << 3;
86 const Log::Severity Log::WARN = 1 << 4;
87 const Log::Severity Log::ERROR = 1 << 5;
88 const Log::Severity Log::TRACE = 1 << 6;
89
90 const Log::Severity Log::DEFAULT = Log::USER | Log::WARN | Log::ERROR | Log::TRACE;
91
92 static AtomicInt Log_Severity_ = Log::DEFAULT;
93
94
95 /* Constructor */
Log()96 Log::Log()
97 : logName_(),
98 openturnsLogSeverityVariableName_("OPENTURNS_LOG_SEVERITY"),
99 p_file_(0),
100 previousMessage_(),
101 count_(0),
102 repeat_(1)
103 {
104 logName_[NONE] = make_prefix( String(TTY::GetColor(TTY::DEFAULT)), "", " " );
105 logName_[ALL] = make_prefix( String(TTY::GetColor(TTY::DEFAULT)), "", "ALL" );
106 logName_[DBG] = make_prefix( String(TTY::GetColor(TTY::DEFAULT)), "", "DBG" );
107 logName_[INFO] = make_prefix( String(TTY::GetColor(TTY::GREENFG)), "", "INF" );
108 logName_[USER] = make_prefix( String(TTY::GetColor(TTY::CYANFG)), "", "USR" );
109 logName_[WARN] = make_prefix( String(TTY::GetColor(TTY::BLUEFG)) + String(TTY::GetColor(TTY::BOLD)), "", "WRN" );
110 logName_[ERROR] = make_prefix( String(TTY::GetColor(TTY::REDFG)) + String(TTY::GetColor(TTY::BOLD)), "", "ERR" );
111 logName_[TRACE] = make_prefix( String(TTY::GetColor(TTY::YELLOWFG)), "", "TRA" );
112
113 initSeverityFromEnvironment();
114
115 }
116
117
118 /* Destructor */
~Log()119 Log::~Log()
120 {
121 delete p_file_;
122 p_file_ = 0;
123 }
124
125
126 /* Set Severity according to Openturns LogSeverity Variable */
initSeverityFromEnvironment()127 void Log::initSeverityFromEnvironment()
128 {
129 const char * logSeverityVariableContent = getenv(openturnsLogSeverityVariableName_);
130 if (logSeverityVariableContent != NULL)
131 {
132 String severityVariableContent(logSeverityVariableContent);
133 Severity theSeverity = Log::NONE;
134
135 const char delim = ',';
136 SignedInteger begPos = 0, endPos;
137 do
138 {
139 // search token
140 endPos = severityVariableContent.find(delim, begPos);
141 if (endPos == static_cast<SignedInteger>(String::npos))
142 endPos = severityVariableContent.size();
143 const String token(severityVariableContent.substr(begPos, endPos - begPos));
144
145 // add severity
146 std::map<Severity, _Prefix >::const_iterator iter;
147 for (iter = logName_.begin(); iter != logName_.end(); ++iter)
148 if (token == iter->second.prefix_)
149 theSeverity |= iter->first;
150
151 // next token
152 begPos = endPos + 1;
153 }
154 while (endPos != static_cast<SignedInteger>(severityVariableContent.size()));
155
156 Show( theSeverity );
157 }
158 }
159
160
161 template<>
MutexLockSingleton(Log & singleton)162 MutexLockSingleton<Log>::MutexLockSingleton( Log & singleton ) throw()
163 : singleton_(singleton)
164 , lock_(Log_InstanceMutex_) {}
165
166
167 /* GetInstance gives a locked access to the singleton */
GetInstance()168 MutexLockSingleton<Log> Log::GetInstance()
169 {
170 static const Log_init force_instantiation;
171 // Log_InstanceMutex_ is always initialized
172 return *Log_P_instance_;
173 }
174
Reset()175 void Log::Reset()
176 {
177 }
178
179
180 /* Log messages according to its relative severity */
Debug(const String & msg)181 void Log::Debug(const String & msg)
182 {
183 GetInstance().lock().push(Entry(DBG, msg));
184 }
185
186
187 /* Log messages according to its relative severity */
Info(const String & msg)188 void Log::Info(const String & msg)
189 {
190 GetInstance().lock().push(Entry(INFO, msg));
191 }
192
193
194
195 /* Log messages according to its relative severity */
User(const String & msg)196 void Log::User(const String & msg)
197 {
198 GetInstance().lock().push(Entry(USER, msg));
199 }
200
201
202
203 /* Log messages according to its relative severity */
Warn(const String & msg)204 void Log::Warn(const String & msg)
205 {
206 GetInstance().lock().push(Entry(WARN, msg));
207 }
208
209
210
211 /* Log messages according to its relative severity */
Error(const String & msg)212 void Log::Error(const String & msg)
213 {
214 GetInstance().lock().push(Entry(ERROR, msg));
215 }
216
217
218 /* Log messages according to its relative severity */
Trace(const String & msg)219 void Log::Trace(const String & msg)
220 {
221 GetInstance().lock().push(Entry(TRACE, msg));
222 }
223
224
225 /* Get/Set the severity flags for the messages logged to the file */
Show(Severity flags)226 void Log::Show(Severity flags)
227 {
228 Log_Severity_ = 0;
229 Log_Severity_.fetchOr(flags);
230 }
231
Flags()232 Log::Severity Log::Flags()
233 {
234 return Log_Severity_.fetchOr(0x00);
235 }
236
237 /* Flush pending messages */
Flush()238 void Log::Flush()
239 {
240 GetInstance().lock().flush();
241 }
242
243
244 /* Does Log show repeated messages or not
245 * If repeat is false then Log shows every messages it receives
246 * even if they are identical to the previous ones.
247 * If repeat is true then Log only shows the first message
248 * and a message counting how much identical messages were
249 * received after that.
250 */
Repeat(Bool r)251 void Log::Repeat(Bool r)
252 {
253 GetInstance().lock().repeat(r);
254 }
255
repeat(Bool r)256 void Log::repeat(Bool r)
257 {
258 repeat_ = r ? 1 : 0;
259 }
260
flush()261 void Log::flush()
262 {
263 printRepeatedMessage( previousMessage_ );
264 previousMessage_ = Entry();
265 count_ = 0;
266 }
267
268
269 /* Append an entry at the end of the list */
push(const Entry & entry)270 void Log::push(const Entry & entry)
271 {
272 std::ostream & os = p_file_ ? *p_file_ : std::clog;
273 if (entry.sev_ & Log::Flags())
274 {
275 if (entry.sev_ != Log::TRACE && repeat_.get() && entry == previousMessage_) ++count_;
276 else
277 {
278 printRepeatedMessage( previousMessage_ );
279 previousMessage_ = entry ;
280 count_ = 0;
281 os << logName_[entry.sev_] << " - " << entry.msg_ << TTY::GetColor(TTY::DEFAULT) << std::endl;
282 }
283 }
284 }
285
printRepeatedMessage(const Entry & entry)286 void Log::printRepeatedMessage(const Entry & entry)
287 {
288 std::ostream & os = p_file_ ? *p_file_ : std::clog;
289 if (count_ > 0)
290 os << logName_[entry.sev_] << " - (previous message repeated " << count_ << " time" << ((count_ == 1) ? "" : "s") << ")" << TTY::GetColor(TTY::DEFAULT) << std::endl;
291 }
292
293 /* Set the name of the log file */
SetFile(const FileName & file)294 void Log::SetFile(const FileName & file)
295 {
296 GetInstance().lock().setFile(file);
297 }
298
299
300 /* Set the name of the log file */
setFile(const FileName & file)301 void Log::setFile(const FileName & file)
302 {
303 push(Entry(INFO, String("Diverting log to file: ") + file));
304 push(Entry(INFO, "*** Log End ***"));
305 delete p_file_;
306 TTY::ShowColors( false );
307 p_file_ = new std::ofstream(file.c_str());
308
309 push(Entry(INFO, "*** Log Beginning ***"));
310 }
311
312 /* Color accessor */
SetColor(const Log::Severity severity,const TTY::Color color)313 void Log::SetColor(const Log::Severity severity,
314 const TTY::Color color)
315 {
316 GetInstance().lock().setColor(severity, String(TTY::GetColor(color)));
317 }
318
SetColor(const Log::Severity severity,const String & color)319 void Log::SetColor(const Log::Severity severity,
320 const String & color)
321 {
322 GetInstance().lock().setColor(severity, color);
323 }
324
325
setColor(const Log::Severity severity,const String & color)326 void Log::setColor(const Log::Severity severity,
327 const String & color)
328 {
329 if (logName_.find(severity) != logName_.end()) logName_[severity].color_ = color;
330 }
331
332 /* Get the color */
GetColor(const Log::Severity severity)333 String Log::GetColor(const Log::Severity severity)
334 {
335 return GetInstance().lock().getColor(severity);
336 }
337
getColor(const Log::Severity severity) const338 String Log::getColor(const Log::Severity severity) const
339 {
340 if (logName_.find(severity) != logName_.end()) return logName_[severity].color_;
341 return String("");
342 }
343
344 END_NAMESPACE_OPENTURNS
345