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