Merge branch 'fixes' into main/rendor-staging
[ryzomcore.git] / nel / src / misc / log.cpp
blob62a2a803cecb2bcf7e33f10418f7152bec3b7523
1 // NeL - MMORPG Framework <http://dev.ryzom.com/projects/nel/>
2 // Copyright (C) 2010-2019 Winch Gate Property Limited
3 //
4 // This program is free software: you can redistribute it and/or modify
5 // it under the terms of the GNU Affero General Public License as
6 // published by the Free Software Foundation, either version 3 of the
7 // License, or (at your option) any later version.
8 //
9 // This program is distributed in the hope that it will be useful,
10 // but WITHOUT ANY WARRANTY; without even the implied warranty of
11 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 // GNU Affero General Public License for more details.
14 // You should have received a copy of the GNU Affero General Public License
15 // along with this program. If not, see <http://www.gnu.org/licenses/>.
17 #include "stdmisc.h"
19 #include "nel/misc/log.h"
21 #ifdef NL_OS_WINDOWS
22 # include <process.h>
23 #else
24 # include <unistd.h>
25 #endif
27 #include "nel/misc/displayer.h"
28 #include "nel/misc/log.h"
29 #include "nel/misc/debug.h"
30 #include "nel/misc/path.h"
32 using namespace std;
34 #ifdef DEBUG_NEW
35 #define new DEBUG_NEW
36 #endif
38 namespace NLMISC
41 string *CLog::_ProcessName = NULL;
43 CLog::CLog( TLogType logType) : _LogType (logType), _FileName(NULL), _Line(-1), _FuncName(NULL), _Mutex("LOG"+toString((uint)logType)), _PosSet(false)
47 void CLog::setDefaultProcessName ()
49 if (_ProcessName == NULL)
51 _ProcessName = (string *)INelContext::getInstance().getSingletonPointer("NLMISC::CLog::_ProcessName");
52 if (_ProcessName == NULL)
54 _ProcessName = new string;
55 INelContext::getInstance().setSingletonPointer("NLMISC::CLog::_ProcessName", _ProcessName);
59 #ifdef NL_OS_WINDOWS
60 if ((*_ProcessName).empty())
62 wchar_t name[1024];
63 GetModuleFileNameW(NULL, name, 1023);
64 (*_ProcessName) = CFile::getFilename(wideToUtf8(name));
66 #else
67 if ((*_ProcessName).empty())
69 *_ProcessName = "<Unknown>";
71 #endif
74 void CLog::setProcessName (const std::string &processName)
76 if (_ProcessName == NULL)
78 _ProcessName = (string *)INelContext::getInstance().getSingletonPointer("NLMISC::CLog::_ProcessName");
79 if (_ProcessName == NULL)
81 _ProcessName = new string;
82 INelContext::getInstance().setSingletonPointer("NLMISC::CLog::_ProcessName", _ProcessName);
86 // keep only filename without path
87 *_ProcessName = CFile::getFilename(processName);
90 void CLog::setPosition (sint line, const char *fileName, const char *funcName)
92 if ( !noDisplayer() )
94 _Mutex.enter();
95 _PosSet++;
96 _FileName = fileName;
97 _Line = line;
98 _FuncName = funcName;
102 /// Symetric to setPosition(). Automatically called by display...(). Do not call if noDisplayer().
103 void CLog::unsetPosition()
105 nlassert( !noDisplayer() );
107 if ( _PosSet > 0 )
109 _FileName = NULL;
110 _Line = -1;
111 _FuncName = NULL;
112 _PosSet--;
113 _Mutex.leave(); // needs setPosition() to have been called
118 void CLog::addDisplayer (IDisplayer *displayer, bool bypassFilter)
120 if (displayer == NULL)
122 // Can't nlwarning because recursive call
123 printf ("Trying to add a NULL displayer\n");
124 return;
127 if (bypassFilter)
129 CDisplayers::iterator idi = std::find (_BypassFilterDisplayers.begin (), _BypassFilterDisplayers.end (), displayer);
130 if (idi == _BypassFilterDisplayers.end ())
132 _BypassFilterDisplayers.push_back (displayer);
134 else
136 nlwarning ("LOG: Couldn't add the displayer, it was already added");
139 else
141 CDisplayers::iterator idi = std::find (_Displayers.begin (), _Displayers.end (), displayer);
142 if (idi == _Displayers.end ())
144 _Displayers.push_back (displayer);
146 else
148 nlwarning ("LOG: Couldn't add the displayer, it was already added");
153 void CLog::removeDisplayer (IDisplayer *displayer)
155 if (displayer == NULL)
157 nlwarning ("LOG: Trying to remove a NULL displayer");
158 return;
161 CDisplayers::iterator idi = std::find (_Displayers.begin (), _Displayers.end (), displayer);
162 if (idi != _Displayers.end ())
164 _Displayers.erase (idi);
167 idi = std::find (_BypassFilterDisplayers.begin (), _BypassFilterDisplayers.end (), displayer);
168 if (idi != _BypassFilterDisplayers.end ())
170 _BypassFilterDisplayers.erase (idi);
175 void CLog::removeDisplayer (const char *displayerName)
177 if (displayerName == NULL || displayerName[0] == '\0')
179 nlwarning ("LOG: Trying to remove an empty displayer name");
180 return;
183 CDisplayers::iterator idi;
184 for (idi = _Displayers.begin (); idi != _Displayers.end ();)
186 if ((*idi)->DisplayerName == displayerName)
188 idi = _Displayers.erase (idi);
190 else
192 idi++;
196 for (idi = _BypassFilterDisplayers.begin (); idi != _BypassFilterDisplayers.end ();)
198 if ((*idi)->DisplayerName == displayerName)
200 idi = _BypassFilterDisplayers.erase (idi);
202 else
204 idi++;
209 IDisplayer *CLog::getDisplayer (const char *displayerName)
211 if (displayerName == NULL || displayerName[0] == '\0')
213 nlwarning ("LOG: Trying to get an empty displayer name");
214 return NULL;
217 CDisplayers::iterator idi;
218 for (idi = _Displayers.begin (); idi != _Displayers.end (); idi++)
220 if ((*idi)->DisplayerName == displayerName)
222 return *idi;
225 for (idi = _BypassFilterDisplayers.begin (); idi != _BypassFilterDisplayers.end (); idi++)
227 if ((*idi)->DisplayerName == displayerName)
229 return *idi;
232 return NULL;
236 * Returns true if the specified displayer is attached to the log object
238 bool CLog::attached(IDisplayer *displayer) const
240 return (find( _Displayers.begin(), _Displayers.end(), displayer ) != _Displayers.end()) ||
241 (find( _BypassFilterDisplayers.begin(), _BypassFilterDisplayers.end(), displayer ) != _BypassFilterDisplayers.end());
245 void CLog::displayString (const char *str)
247 const char *disp = NULL;
248 TDisplayInfo localargs, *args = NULL;
250 setDefaultProcessName ();
252 if(strchr(str,'\n') == NULL)
254 if (TempString.empty())
256 time (&TempArgs.Date);
257 TempArgs.LogType = _LogType;
258 TempArgs.ProcessName = *_ProcessName;
259 TempArgs.ThreadId = getThreadId();
260 TempArgs.FileName = _FileName;
261 TempArgs.Line = _Line;
262 TempArgs.FuncName = _FuncName;
263 TempArgs.CallstackAndLog.clear();
265 TempString = str;
267 else
269 TempString += str;
271 return;
273 else
275 if (TempString.empty())
277 time (&localargs.Date);
278 localargs.LogType = _LogType;
279 localargs.ProcessName = *_ProcessName;
280 localargs.ThreadId = getThreadId();
281 localargs.FileName = _FileName;
282 localargs.Line = _Line;
283 localargs.FuncName = _FuncName;
284 localargs.CallstackAndLog.clear();
286 disp = str;
287 args = &localargs;
289 else
291 TempString += str;
292 disp = TempString.c_str();
293 args = &TempArgs;
297 // send to all bypass filter displayers
298 for (CDisplayers::iterator idi=_BypassFilterDisplayers.begin(); idi!=_BypassFilterDisplayers.end(); idi++ )
300 (*idi)->display( *args, disp );
303 // get the log at the last minute to be sure to have everything
304 if(args->LogType == LOG_ERROR || args->LogType == LOG_ASSERT)
306 getCallStackAndLog (args->CallstackAndLog, 4);
309 if (passFilter (disp))
311 // Send to the attached displayers
312 for (CDisplayers::iterator idi=_Displayers.begin(); idi!=_Displayers.end(); idi++ )
314 (*idi)->display( *args, disp );
317 TempString.clear();
318 unsetPosition();
323 * Display the string with decoration and final new line to all attached displayers
325 #ifdef NL_OS_WINDOWS
326 void CLog::_displayNL (const char *format, ...)
327 #else
328 void CLog::displayNL (const char *format, ...)
329 #endif
331 if ( noDisplayer() )
333 return;
336 char *str;
337 NLMISC_CONVERT_VARGS (str, format, 1024/*NLMISC::MaxCStringSize*/);
339 if (strlen(str)<1024/*NLMISC::MaxCStringSize*/-1)
340 strcat (str, "\n");
341 else
342 str[1024/*NLMISC::MaxCStringSize*/-2] = '\n';
344 displayString (str);
348 * Display the string with decoration to all attached displayers
350 #ifdef NL_OS_WINDOWS
351 void CLog::_display (const char *format, ...)
352 #else
353 void CLog::display (const char *format, ...)
354 #endif
356 if ( noDisplayer() )
358 return;
361 char *str;
362 NLMISC_CONVERT_VARGS (str, format, 1024/*NLMISC::MaxCStringSize*/);
364 displayString (str);
367 void CLog::displayRawString (const char *str)
369 const char *disp = NULL;
370 TDisplayInfo localargs, *args = NULL;
372 setDefaultProcessName ();
374 if(strchr(str,'\n') == NULL)
376 if (TempString.empty())
378 localargs.Date = 0;
379 localargs.LogType = CLog::LOG_NO;
380 localargs.ProcessName.clear();
381 localargs.ThreadId = 0;
382 localargs.FileName = NULL;
383 localargs.Line = -1;
384 localargs.CallstackAndLog.clear();
386 TempString = str;
388 else
390 TempString += str;
392 return;
394 else
396 if (TempString.empty())
398 localargs.Date = 0;
399 localargs.LogType = CLog::LOG_NO;
400 localargs.ProcessName.clear();
401 localargs.ThreadId = 0;
402 localargs.FileName = NULL;
403 localargs.Line = -1;
404 localargs.CallstackAndLog.clear();
406 disp = str;
407 args = &localargs;
409 else
411 TempString += str;
412 disp = TempString.c_str();
413 args = &TempArgs;
417 // send to all bypass filter displayers
418 for (CDisplayers::iterator idi=_BypassFilterDisplayers.begin(); idi!=_BypassFilterDisplayers.end(); idi++ )
420 (*idi)->display( *args, disp );
423 // get the log at the last minute to be sure to have everything
424 if(args->LogType == LOG_ERROR || args->LogType == LOG_ASSERT)
426 getCallStackAndLog (args->CallstackAndLog, 4);
429 if ( passFilter( disp ) )
431 // Send to the attached displayers
432 for ( CDisplayers::iterator idi=_Displayers.begin(); idi!=_Displayers.end(); idi++ )
434 (*idi)->display( *args, disp );
437 TempString.clear();
438 unsetPosition();
442 * Display a string (and nothing more) to all attached displayers
444 #ifdef NL_OS_WINDOWS
445 void CLog::_displayRawNL( const char *format, ... )
446 #else
447 void CLog::displayRawNL( const char *format, ... )
448 #endif
450 if ( noDisplayer() )
452 return;
455 char *str;
456 NLMISC_CONVERT_VARGS (str, format, 1024/*NLMISC::MaxCStringSize*/);
458 if (strlen(str)<1024/*NLMISC::MaxCStringSize*/-1)
459 strcat (str, "\n");
460 else
461 str[1024/*NLMISC::MaxCStringSize*/-2] = '\n';
463 displayRawString(str);
467 * Display a string (and nothing more) to all attached displayers
469 #ifdef NL_OS_WINDOWS
470 void CLog::_displayRaw( const char *format, ... )
471 #else
472 void CLog::displayRaw( const char *format, ... )
473 #endif
475 if ( noDisplayer() )
477 return;
480 char *str;
481 NLMISC_CONVERT_VARGS (str, format, 1024/*NLMISC::MaxCStringSize*/);
483 displayRawString(str);
487 #ifdef NL_OS_WINDOWS
488 void CLog::_forceDisplayRaw (const char *format, ...)
489 #else
490 void CLog::forceDisplayRaw (const char *format, ...)
491 #endif
493 if ( noDisplayer() )
495 return;
498 char *str;
499 NLMISC_CONVERT_VARGS (str, format, 1024/*NLMISC::MaxCStringSize*/);
501 TDisplayInfo args;
502 CDisplayers::iterator idi;
504 // send to all bypass filter displayers
505 for (idi=_BypassFilterDisplayers.begin(); idi!=_BypassFilterDisplayers.end(); idi++ )
507 (*idi)->display( args, str );
510 // Send to the attached displayers
511 for ( idi=_Displayers.begin(); idi!=_Displayers.end(); idi++ )
513 (*idi)->display( args, str );
520 * Returns true if the string must be logged, according to the current filter
522 bool CLog::passFilter( const char *filter )
524 bool yes = _PositiveFilter.empty();
526 bool found;
527 list<string>::iterator ilf;
529 // 1. Positive filter
530 for ( ilf=_PositiveFilter.begin(); ilf!=_PositiveFilter.end(); ++ilf )
532 found = ( strstr( filter, (*ilf).c_str() ) != NULL );
533 if ( found )
535 yes = true; // positive filter passed (no need to check another one)
536 break;
538 // else try the next one
540 if ( ! yes )
542 return false; // positive filter not passed
545 // 2. Negative filter
546 for ( ilf=_NegativeFilter.begin(); ilf!=_NegativeFilter.end(); ++ilf )
548 found = ( strstr( filter, (*ilf).c_str() ) != NULL );
549 if ( found )
551 return false; // negative filter not passed (no need to check another one)
554 return true; // negative filter passed
559 * Removes a filter by name. Returns true if it was found.
561 void CLog::removeFilter( const char *filterstr )
563 if (filterstr == NULL)
565 _PositiveFilter.clear();
566 _NegativeFilter.clear();
567 //displayNL ("CLog::addNegativeFilter('%s')", filterstr);
569 else
571 _PositiveFilter.remove( filterstr );
572 _NegativeFilter.remove( filterstr );
573 //displayNL ("CLog::removeFilter('%s')", filterstr);
577 void CLog::displayFilter( CLog &log )
579 std::list<std::string>::iterator it;
580 log.displayNL ("Positive Filter(s):");
581 for (it = _PositiveFilter.begin (); it != _PositiveFilter.end (); it++)
583 log.displayNL ("'%s'", (*it).c_str());
585 log.displayNL ("Negative Filter(s):");
586 for (it = _NegativeFilter.begin (); it != _NegativeFilter.end (); it++)
588 log.displayNL ("'%s'", (*it).c_str());
592 void CLog::addPositiveFilter( const char *filterstr )
594 //displayNL ("CLog::addPositiveFilter('%s')", filterstr);
595 _PositiveFilter.push_back( filterstr );
598 void CLog::addNegativeFilter( const char *filterstr )
600 //displayNL ("CLog::addNegativeFilter('%s')", filterstr);
601 _NegativeFilter.push_back( filterstr );
604 void CLog::resetFilters()
606 //displayNL ("CLog::resetFilter()");
607 _PositiveFilter.clear();
608 _NegativeFilter.clear();
611 /// Do not call this unless you know why you're doing it, it kills the debug/log system!
612 void CLog::releaseProcessName()
614 if (INelContext::isContextInitialised())
616 INelContext::getInstance().releaseSingletonPointer("NLMISC::CLog::_ProcessName", _ProcessName);
619 if (_ProcessName)
621 delete _ProcessName;
622 _ProcessName = NULL;
626 } // NLMISC