1 // NeL - MMORPG Framework <http://dev.ryzom.com/projects/nel/>
2 // Copyright (C) 2010-2019 Winch Gate Property Limited
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.
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/>.
19 #include "nel/misc/log.h"
27 #include "nel/misc/displayer.h"
28 #include "nel/misc/log.h"
29 #include "nel/misc/debug.h"
30 #include "nel/misc/path.h"
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
);
60 if ((*_ProcessName
).empty())
63 GetModuleFileNameW(NULL
, name
, 1023);
64 (*_ProcessName
) = CFile::getFilename(wideToUtf8(name
));
67 if ((*_ProcessName
).empty())
69 *_ProcessName
= "<Unknown>";
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
)
102 /// Symetric to setPosition(). Automatically called by display...(). Do not call if noDisplayer().
103 void CLog::unsetPosition()
105 nlassert( !noDisplayer() );
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");
129 CDisplayers::iterator idi
= std::find (_BypassFilterDisplayers
.begin (), _BypassFilterDisplayers
.end (), displayer
);
130 if (idi
== _BypassFilterDisplayers
.end ())
132 _BypassFilterDisplayers
.push_back (displayer
);
136 nlwarning ("LOG: Couldn't add the displayer, it was already added");
141 CDisplayers::iterator idi
= std::find (_Displayers
.begin (), _Displayers
.end (), displayer
);
142 if (idi
== _Displayers
.end ())
144 _Displayers
.push_back (displayer
);
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");
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");
183 CDisplayers::iterator idi
;
184 for (idi
= _Displayers
.begin (); idi
!= _Displayers
.end ();)
186 if ((*idi
)->DisplayerName
== displayerName
)
188 idi
= _Displayers
.erase (idi
);
196 for (idi
= _BypassFilterDisplayers
.begin (); idi
!= _BypassFilterDisplayers
.end ();)
198 if ((*idi
)->DisplayerName
== displayerName
)
200 idi
= _BypassFilterDisplayers
.erase (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");
217 CDisplayers::iterator idi
;
218 for (idi
= _Displayers
.begin (); idi
!= _Displayers
.end (); idi
++)
220 if ((*idi
)->DisplayerName
== displayerName
)
225 for (idi
= _BypassFilterDisplayers
.begin (); idi
!= _BypassFilterDisplayers
.end (); idi
++)
227 if ((*idi
)->DisplayerName
== displayerName
)
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();
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();
292 disp
= TempString
.c_str();
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
);
323 * Display the string with decoration and final new line to all attached displayers
326 void CLog::_displayNL (const char *format
, ...)
328 void CLog::displayNL (const char *format
, ...)
337 NLMISC_CONVERT_VARGS (str
, format
, 1024/*NLMISC::MaxCStringSize*/);
339 if (strlen(str
)<1024/*NLMISC::MaxCStringSize*/-1)
342 str
[1024/*NLMISC::MaxCStringSize*/-2] = '\n';
348 * Display the string with decoration to all attached displayers
351 void CLog::_display (const char *format
, ...)
353 void CLog::display (const char *format
, ...)
362 NLMISC_CONVERT_VARGS (str
, format
, 1024/*NLMISC::MaxCStringSize*/);
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())
379 localargs
.LogType
= CLog::LOG_NO
;
380 localargs
.ProcessName
.clear();
381 localargs
.ThreadId
= 0;
382 localargs
.FileName
= NULL
;
384 localargs
.CallstackAndLog
.clear();
396 if (TempString
.empty())
399 localargs
.LogType
= CLog::LOG_NO
;
400 localargs
.ProcessName
.clear();
401 localargs
.ThreadId
= 0;
402 localargs
.FileName
= NULL
;
404 localargs
.CallstackAndLog
.clear();
412 disp
= TempString
.c_str();
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
);
442 * Display a string (and nothing more) to all attached displayers
445 void CLog::_displayRawNL( const char *format
, ... )
447 void CLog::displayRawNL( const char *format
, ... )
456 NLMISC_CONVERT_VARGS (str
, format
, 1024/*NLMISC::MaxCStringSize*/);
458 if (strlen(str
)<1024/*NLMISC::MaxCStringSize*/-1)
461 str
[1024/*NLMISC::MaxCStringSize*/-2] = '\n';
463 displayRawString(str
);
467 * Display a string (and nothing more) to all attached displayers
470 void CLog::_displayRaw( const char *format
, ... )
472 void CLog::displayRaw( const char *format
, ... )
481 NLMISC_CONVERT_VARGS (str
, format
, 1024/*NLMISC::MaxCStringSize*/);
483 displayRawString(str
);
488 void CLog::_forceDisplayRaw (const char *format
, ...)
490 void CLog::forceDisplayRaw (const char *format
, ...)
499 NLMISC_CONVERT_VARGS (str
, format
, 1024/*NLMISC::MaxCStringSize*/);
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();
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
);
535 yes
= true; // positive filter passed (no need to check another one)
538 // else try the next one
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
);
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);
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
);