Make ScopeTracker thread safe via TLS (thread local storage)
[libi2ncommon] / src / logfunc.cpp
CommitLineData
0e23f538
TJ
1/*
2The software in this package is distributed under the GNU General
3Public License version 2 (with a special exception described below).
4
5A copy of GNU General Public License (GPL) is included in this distribution,
6in the file COPYING.GPL.
7
8As a special exception, if other files instantiate templates or use macros
9or inline functions from this file, or you compile this file and link it
10with other works to produce a work based on this file, this file
11does not by itself cause the resulting work to be covered
12by the GNU General Public License.
13
14However the source code for this file must still be made available
15in accordance with section (3) of the GNU General Public License.
16
17This exception does not invalidate any other reasons why a work based
18on this file might be covered by the GNU General Public License.
19*/
b6fb82a2
RP
20/** @file
21 * @brief implementaton of logging functions.
22 *
23 * @copyright © Copyright 2007-2008 by Intra2net AG
57924725
TJ
24 *
25 * @note Don't call loggers from global constructed objects
26 * as this depends on the global object construction sequence.
b6fb82a2
RP
27 */
28
29#include "logfunc.hpp"
30
31#include <syslog.h>
32#include <cstdlib>
33#include <ctime>
34#include <algorithm>
35#include <iostream>
36#include <fstream>
37#include <unistd.h>
5efd35b1 38#include <string.h>
b6fb82a2 39#include <boost/shared_ptr.hpp>
57924725
TJ
40#include <boost/thread/recursive_mutex.hpp>
41#include <boost/thread/thread.hpp>
b6fb82a2
RP
42
43#include <stringfunc.hxx>
44#include <filefunc.hxx>
45
46
47namespace I2n
48{
49namespace Logger
50{
51
b6fb82a2
RP
52/**
53 * @brief the global logger instance.
54 *
55 * This may be used in all cases wheer a specialized logger is not available
56 * or shouldn't be used for some reason.
57 */
58PartLogger GlobalLogger("");
59
60
61namespace
62{
63
f4fcef65
TJ
64/// Global lock for the logger. Used during all syslog operations
65/// and modification of our shared local variables.
66static boost::recursive_mutex LoggerLock;
67
b6fb82a2
RP
68/*
69** local globals:
70*/
71
57924725
TJ
72static std::string g_ident;
73static Facility g_facility;
74static bool g_syslog_opened = false;
75static int g_max_level= LogLevel::Warning;
b6fb82a2 76
57924725 77static bool g_stderr_log = false;
b6fb82a2 78
57924725
TJ
79static std::string g_log_file_name;
80static boost::shared_ptr< std::ofstream > g_log_stream_ptr;
b6fb82a2
RP
81
82/**
83 * @brief lookup array for translating our log levels to syslog level.
84 */
57924725 85static int loglevel_2_syslog_level[ LogLevel::_LogLevel_END ] = {
f61aeac1
RP
86 LOG_EMERG,
87 LOG_ALERT,
88 LOG_CRIT,
89 LOG_ERR,
90 LOG_WARNING,
91 LOG_NOTICE,
92 LOG_INFO,
93 LOG_DEBUG
b6fb82a2
RP
94};
95
96
97/**
98 * @brief lookup array for translating our log levels to a short (4 character) tag.
99 *
100 * These tags are used when logs are written to stderr or into a log file.
101 */
57924725 102static std::string loglevel_2_short_tag[ LogLevel::_LogLevel_END ] = {
f61aeac1
RP
103 "EMRG",
104 "ALRT",
105 "CRIT",
106 "ERR ",
107 "WARN",
108 "NOTE",
109 "INFO",
110 "DBUG"
b6fb82a2
RP
111};
112
113
114/**
115 * @brief a copy of the current identifier used for syslog.
116 *
117 * Keeping a copy of this ident is necessary since openlog doen't copy it's first
118 * argument but copies only the pointer! (what a **censored**!)
119 */
57924725 120static char* syslog_ident= NULL;
b6fb82a2
RP
121
122
123/*
124** functions:
125*/
126
127/**
128 * @brief close syslog.
129 */
130void close_syslog()
131{
57924725
TJ
132 boost::recursive_mutex::scoped_lock lock(LoggerLock);
133
f61aeac1
RP
134 if (g_syslog_opened)
135 {
136 closelog();
137 if (syslog_ident)
138 {
139 free(syslog_ident);
140 syslog_ident= NULL;
141 }
142 g_syslog_opened= false;
143 }
b6fb82a2
RP
144} // eo close_syslog()
145
146
147/**
148 * @brief open syslog.
149 */
150void open_syslog()
151{
57924725
TJ
152 boost::recursive_mutex::scoped_lock lock(LoggerLock);
153
f61aeac1
RP
154 close_syslog();
155 syslog_ident= strdup(g_ident.c_str());
156 openlog( syslog_ident, LOG_CONS|LOG_PID, g_facility);
157 g_syslog_opened= true;
b6fb82a2
RP
158} // eo open_syslog()
159
160
161/**
162 * @brief get syslog level from internal log level.
163 * @param level log level.
164 * @return syslog level
165 */
166int get_syslog_level( int level )
167{
57924725
TJ
168 // Note: Thread safe
169
d0d6eef7 170 if (level >=0 && level < LogLevel::_LogLevel_END)
f61aeac1
RP
171 {
172 return loglevel_2_syslog_level[level];
173 }
174 return (level<0) ? (LOG_EMERG) : (LOG_DEBUG);
b6fb82a2
RP
175} // eo get_syslog_level(int)
176
177
178/**
179 * @brief get a short tag for the log level (/ message type)
180 * @param level the log level
181 * @return a short tag for the level.
182 */
183std::string get_level_tag( int level )
184{
57924725
TJ
185 // Note: Thread safe
186
d0d6eef7 187 if (level >=0 && level < LogLevel::_LogLevel_END)
f61aeac1
RP
188 {
189 return loglevel_2_short_tag[level];
190 }
191 return (level<0) ? loglevel_2_short_tag[0] : loglevel_2_short_tag[ LogLevel::_LogLevel_END -1 ];
b6fb82a2
RP
192} // eo get_level_tag(int)
193
194
195/**
196 * @brief the "real" log function which logs a message at a given level.
197 * @param level the log level to log the message at.
198 * @param msg the message.
199 *
200 * Write the message to every enabled log channel.
201 *
202 * If syslog is enabled the message is passed unmodified to syslog.
203 *
204 * If a stream log is enabled (stderr or file) then the message is prepended with date, time
205 * and process information (like syslog does). The message is splitted at line ends and
206 * consecutive lines are indented.
207 */
208void log_msg( int level, const std::string& msg)
209{
57924725
TJ
210 boost::recursive_mutex::scoped_lock lock(LoggerLock);
211
f61aeac1
RP
212 if (not g_syslog_opened and not g_stderr_log and not g_log_stream_ptr)
213 {
214 // if nothing is opened for logging: we activate syslog!
215 enable_syslog(true);
216 }
217
218 if (g_syslog_opened)
219 {
220 ::syslog( get_syslog_level(level), "%s", msg.c_str());
221 }
222 // file(/stream) logging:
223 if (g_stderr_log or g_log_stream_ptr) // add more log "enabled" expressions here...
224 {
225 // here we need must do something more with the msg...
226 std::string new_msg;
227 std::string prefix;
228 {
b6fb82a2 229 std::ostringstream ostr;
f61aeac1 230 // add time stamp (syslog like: "Mon DD HH:MM:SS") :
b6fb82a2 231 {
f61aeac1
RP
232 time_t t = time(NULL);
233 char buffer[32];
234 std::strftime(buffer, sizeof(buffer),"%b %d %H:%M:%S ", std::localtime(&t));
235 ostr << buffer;
b6fb82a2 236 }
f61aeac1
RP
237 ostr << get_level_tag(level) << " ";
238 ostr << g_ident << "[" << getpid() << "]: ";
239 prefix= ostr.str();
240 }
241 {
242 {
243 std::string indent_string(prefix.size(), ' ');
244 std::list< std::string > parts;
245 split_string( chomp(msg,"\n"), parts, "\n");
246 std::ostringstream ostr;
247 ostr << prefix;
248 for(std::list< std::string >::const_iterator it= parts.begin();
249 it != parts.end();
250 ++it)
251 {
252 if (it != parts.begin())
253 {
254 ostr << indent_string;
255 }
256 ostr << *it << std::endl;
257 }
258 new_msg= ostr.str();
259 }
260 }
261 if (g_stderr_log)
262 {
263 std::cerr << new_msg;
264 }
265 if (g_log_stream_ptr)
266 {
267 *g_log_stream_ptr << new_msg << std::flush;
268 }
269 }
b6fb82a2
RP
270} // eo log_msg
271
272
273/**
274 * @brief "real" log function for part messages.
275 * @param level the log level.
276 * @param part the part(/module) name(/id)
277 * @param msg the log message.
278 *
279 * basically calls @a log(), but prepends the part (if not empty) in square brackets to the message.
280 */
281void log_part_msg(
f61aeac1
RP
282 int level,
283 const std::string& part,
284 const std::string& msg)
285{
57924725
TJ
286 // Note: Locking is done in log_msg()
287
f61aeac1
RP
288 if (!part.empty())
289 {
290 std::ostringstream ostr;
291 ostr << "[" << part << "] " << msg;
292 log_msg(level, ostr.str());
293 }
294 else
295 {
296 log_msg(level, msg);
297 }
b6fb82a2
RP
298} // eo log_part_msg(int,const std::string&,const std::string&)
299
300
b5d5a346
RP
301/**
302 * @brief returns the name of the program (/binary)
303 * @return the program name if it could be determined; generated name else.
304 *
305 * Tries to determine the name of the binary.
306 *
307 * If no name could be determined, one is built.
308 */
309std::string get_program_name()
310{
57924725
TJ
311 // Note: Thread safe
312
b5d5a346
RP
313 std::string result;
314 // determine the program name:
315 {
316 // try to determine the name using the exe link:
317 std::string exe_path;
318 {
319 std::ostringstream ostr;
320 ostr << "/proc/" << ::getpid() << "/exe";
321 exe_path= ostr.str();
322 }
323 std::string binary_path= read_link(exe_path);
324 if (!binary_path.empty())
325 {
326 result= basename(binary_path);
327 }
328 }
329 if (result.empty())
330 {
331 // no program name found up to this point.
332 // make a name (as fallback solution):
333 std::ostringstream ostr;
334 ostr << "prg-" << ::getpid();
335 result= ostr.str();
336 }
337 return result;
338} // eo get_program_name
339
340
b6fb82a2
RP
341
342void _cleanup()
343{
57924725 344 // Note: Locking is done in close_syslog();
f61aeac1
RP
345 close_syslog();
346 //TODO other cleanups?
b6fb82a2
RP
347} // _cleanup
348
349
350class __Initializer
351{
f61aeac1
RP
352 public:
353 __Initializer()
354 {
355 std::atexit( _cleanup );
356 }
b6fb82a2
RP
357} __initialize;
358
359
360} // eo namespace <anonymous>
361
362
363
364/*
365** implementation of Facility
366*/
367
368
369const int Facility::AuthPriv= LOG_AUTH;
370const int Facility::Cron = LOG_CRON;
371const int Facility::Daemon = LOG_DAEMON;
372const int Facility::Kern = LOG_KERN;
373const int Facility::Mail = LOG_MAIL;
374const int Facility::News = LOG_NEWS;
375const int Facility::Syslog = LOG_SYSLOG;
376const int Facility::User = LOG_USER;
377const int Facility::UUCP = LOG_UUCP;
378const int Facility::Local0 = LOG_LOCAL0;
379const int Facility::Local1 = LOG_LOCAL1;
380const int Facility::Local2 = LOG_LOCAL2;
381const int Facility::Local3 = LOG_LOCAL3;
382const int Facility::Local4 = LOG_LOCAL4;
383const int Facility::Local5 = LOG_LOCAL5;
384const int Facility::Local6 = LOG_LOCAL6;
385const int Facility::Local7 = LOG_LOCAL7;
386
387
388
389/*
390** implementation of PartLogger::LogHelper
391*/
392
393PartLogger::LogHelper::LogHelper(PartLogger& logger, int level, const SourceLocation& loc)
394: Logger(logger)
395, Level(level)
396, Location(loc)
397{
f61aeac1 398 StreamPtr.reset(new std::ostringstream());
b6fb82a2
RP
399} // eo PartLogger::LogHelper::LogHelper(PartLogger&,int)
400
401PartLogger::LogHelper::LogHelper(const LogHelper& helper)
402: Logger(helper.Logger)
403, Level(helper.Level)
404, Location(helper.Location)
405, StreamPtr(helper.StreamPtr)
406{
407} // eo PartLogger::LogHelper::LogHelper(const LogHelper&)
408
409
410PartLogger::LogHelper::~LogHelper()
411{
f61aeac1
RP
412 if (StreamPtr.get())
413 {
414 if (Location)
415 {
416 //*m_stream_ptr << " at " << m_loc.Line << " in " << m_loc.FunctionName;
417 *StreamPtr << " @" << Location.get_location_tag();
418 }
419 std::string msg(StreamPtr->str());
420 if (!msg.empty())
421 {
422 Logger.log(Level,msg);
423 }
424 }
b6fb82a2
RP
425} // eo PartLogger::LogHelper::~LogHelper
426
427
428/*
429** implementation of PartLogger
430*/
431
432/**
433 * constructor for a part logger.
434 * @param part name of the part (module name) using the logger instance.
435 */
436PartLogger::PartLogger(const std::string& part)
437: Part(part)
438{
439} // eo PartLogger::PartLogger(const std.:string&)
440
441
442/**
443 * @brief constructor for a part logger at module level.
444 * @param loc the source location where the PartLogger is constructed.
445 *
446 * The part name is derived from the filename given with the source location by
447 * using the basename and cutting off the C++ file suffix (if it is a well known one;
448 * currently known extensions: cpp, cxx, c++, cc, C).
449 */
450PartLogger::PartLogger( const SourceLocation& loc )
451{
f61aeac1
RP
452 if (loc.Line>0 && ! loc.File.empty())
453 {
454 std::string str= basename(loc.File);
455 Part= remove_suffix(str,".cpp");
456 if (Part == str) Part= remove_suffix(str,".cxx");
457 if (Part == str) Part= remove_suffix(str,".c++");
458 if (Part == str) Part= remove_suffix(str,".cc");
459 if (Part == str) Part= remove_suffix(str,".C");
460 }
461 else
462 {
463 Part="Unknown";
464 }
b6fb82a2
RP
465}// PartLogger::PartLogger(const SourceLocation&)
466
467
468PartLogger::~PartLogger()
469{
470}
471
472
473/**
474 * generic log function.
475 * @param level the log level.
476 * @param msg the log message.
477 */
478void PartLogger::log(int level, const std::string msg)
479{
57924725
TJ
480 boost::recursive_mutex::scoped_lock lock(LoggerLock);
481
f61aeac1
RP
482 if (level <= g_max_level)
483 {
484 log_part_msg(level, Part, msg);
485 }
b6fb82a2
RP
486} // eo PartLogger::log(int,const std::string);
487
488
489void PartLogger::fatal(const std::string& msg)
490{
f61aeac1 491 log(LOG_EMERG,msg);
b6fb82a2
RP
492} // eo PartLogger::fatal(const std::string&)
493
494
495void PartLogger::alert(const std::string& msg)
496{
f61aeac1 497 log(LOG_ALERT,msg);
b6fb82a2
RP
498} // eo PartLogger::alert(const std::string&)
499
500
501void PartLogger::critical(const std::string& msg)
502{
f61aeac1 503 log(LOG_CRIT,msg);
b6fb82a2
RP
504} // eo PartLogger::critical(const std::string&)
505
506
507void PartLogger::error(const std::string& msg)
508{
f61aeac1 509 log(LOG_ERR, msg);
b6fb82a2
RP
510} // eo PartLogger::error(const std::string&)
511
512
513void PartLogger::warning(const std::string& msg)
514{
f61aeac1 515 log(LOG_WARNING, msg);
b6fb82a2
RP
516} // eo PartLogger::warning(const std::string&)
517
518
519void PartLogger::notice(const std::string& msg)
520{
f61aeac1 521 log(LOG_NOTICE, msg);
b6fb82a2
RP
522} // eo PartLogger::notice(const std::string&)
523
524
525void PartLogger::info(const std::string& msg)
526{
f61aeac1 527 log(LOG_INFO, msg);
b6fb82a2
RP
528} // eo PartLogger::info(const std::string&)
529
530
531void PartLogger::debug(const std::string& msg)
532{
f61aeac1 533 log(LOG_DEBUG, msg);
b6fb82a2
RP
534} // eo PartLogger::debug(const std::string&)
535
536
537
538PartLogger::LogHelper PartLogger::fatal(const SourceLocation& loc)
539{
f61aeac1 540 return PartLogger::LogHelper(*this,LOG_EMERG,loc);
b6fb82a2
RP
541} // eo PartLogger::fatal(const SourceLocation&)
542
543
544PartLogger::LogHelper PartLogger::alert(const SourceLocation& loc)
545{
f61aeac1 546 return PartLogger::LogHelper(*this,LOG_ALERT,loc);
b6fb82a2
RP
547} // eo PartLogger::alert(const SourceLocation&)
548
549
550PartLogger::LogHelper PartLogger::critical(const SourceLocation& loc)
551{
f61aeac1 552 return PartLogger::LogHelper(*this,LOG_CRIT,loc);
b6fb82a2
RP
553} // eo PartLogger::critical(const SourceLocation&)
554
555
556PartLogger::LogHelper PartLogger::error(const SourceLocation& loc)
557{
f61aeac1 558 return PartLogger::LogHelper(*this,LOG_ERR,loc);
b6fb82a2
RP
559} // eo PartLogger::error(const SourceLocation&)
560
561
562PartLogger::LogHelper PartLogger::warning(const SourceLocation& loc)
563{
f61aeac1 564 return PartLogger::LogHelper(*this,LOG_WARNING,loc);
b6fb82a2
RP
565} // eo PartLogger::warning(const SourceLocation&)
566
567
568PartLogger::LogHelper PartLogger::notice(const SourceLocation& loc)
569{
f61aeac1 570 return PartLogger::LogHelper(*this,LOG_NOTICE,loc);
b6fb82a2
RP
571} // eo PartLogger::notice(const SourceLocation&)
572
573
574PartLogger::LogHelper PartLogger::info(const SourceLocation& loc)
575{
f61aeac1 576 return PartLogger::LogHelper(*this,LOG_INFO,loc);
b6fb82a2
RP
577} // eo PartLogger::info(const SourceLocation&)
578
579
580PartLogger::LogHelper PartLogger::debug(const SourceLocation& loc)
581{
f61aeac1 582 return PartLogger::LogHelper(*this,LOG_DEBUG,loc);
b6fb82a2
RP
583} // eo PartLogger::debug(const SourceLocation&)
584
585/*
586**
587*/
588
589
590/**
591 * enable logging to syslog with a name and a facility.
592 * @param name the name used as ident.
593 * @param facility the facility which should be used.
594 */
595void enable_syslog( const std::string& name, Facility facility )
596{
57924725
TJ
597 boost::recursive_mutex::scoped_lock lock(LoggerLock);
598
f61aeac1
RP
599 close_syslog();
600 g_ident= name;
601 g_facility= facility;
602 open_syslog();
b6fb82a2
RP
603} // eo enable_syslog(const std::string,Facility)
604
605
606/**
607 * enable logging to syslog with a facility.
608 * The ident is used from a previous call or (if none was set) is
609 * determined by reading the program path from /proc/\<pid\>/exe.
610 * @param facility the facility which should be used.
611 */
612void enable_syslog( Facility facility )
613{
57924725
TJ
614 boost::recursive_mutex::scoped_lock lock(LoggerLock);
615
f61aeac1
RP
616 if (g_ident.empty())
617 {
b5d5a346 618 g_ident= get_program_name();
f61aeac1
RP
619 }
620 close_syslog();
621 g_facility = facility;
622 open_syslog();
b6fb82a2
RP
623} // eo enable_syslog(Facility)
624
625
626/**
627 * enable or disable logging to syslog.
628 * @param enable whether the logging to syslog should be enabled or not.
629 */
630void enable_syslog( bool enable )
631{
57924725
TJ
632 boost::recursive_mutex::scoped_lock lock(LoggerLock);
633
f61aeac1
RP
634 if (enable)
635 {
636 if (!g_syslog_opened)
637 {
638 enable_syslog( g_facility );
639 }
640 }
641 else // ! enable
642 {
643 close_syslog();
644 }
b6fb82a2
RP
645} // eo enable_syslog(bool)
646
647
648/**
649 * enable/ disable loggin to stderr.
650 * @param enable whether to enable or disable logging to stderr.
651 */
652void enable_stderr_log(bool enable)
653{
57924725
TJ
654 boost::recursive_mutex::scoped_lock lock(LoggerLock);
655
f61aeac1 656 g_stderr_log= enable;
b6fb82a2
RP
657} // eo enableStderr;
658
659
660
661/**
662 * enable logging to a file.
663 * @param name path to the file.
664 *
665 * @note only one log file can be use at a time.
666 */
667void enable_log_file( const std::string& name )
668{
57924725
TJ
669 boost::recursive_mutex::scoped_lock lock(LoggerLock);
670
f61aeac1
RP
671 g_log_file_name= name;
672 g_log_stream_ptr.reset( new std::ofstream() );
673 g_log_stream_ptr->open( name.c_str(), std::ios::out|std::ios::app );
674 //std::cerr << "### opened \"" << name << "\"" << g_log_stream_ptr->good() << std::endl;
b6fb82a2
RP
675} // eo enable_log_file(const std::string&)
676
677
678/**
679 * enable or disable loggin to a file.
b5d5a346
RP
680 * if a logfile was already set by a previous call to enable_log_file(const std::string&)
681 * that one is used; else it logs to <tt>/var/log/</tt><em>program name</em><tt>.log</tt>.
b6fb82a2
RP
682 * @param enable whether to enable or disable logging to a file.
683 */
684void enable_log_file( bool enable )
685{
57924725
TJ
686 boost::recursive_mutex::scoped_lock lock(LoggerLock);
687
f61aeac1
RP
688 if (enable)
689 {
b5d5a346
RP
690 if (g_log_file_name.empty())
691 {
692 std::ostringstream ostr;
693 ostr << "/var/log/" << get_program_name() << ".log";
694 enable_log_file( ostr.str() );
695 }
696 else
f61aeac1
RP
697 {
698 enable_log_file( g_log_file_name );
699 }
700 }
701 else // ! enable
702 {
703 g_log_stream_ptr.reset();
704 }
b6fb82a2
RP
705} // eo enable_log_file(bool)
706
707
b5d5a346
RP
708/**
709 * @brief returns if loging to file is enabled and active.
710 * @return @a true if logfile is enabled and opened.
711 */
712bool is_logging_to_file()
713{
57924725
TJ
714 boost::recursive_mutex::scoped_lock lock(LoggerLock);
715
b5d5a346
RP
716 return g_log_stream_ptr and g_log_stream_ptr->good();
717} // eo is_logging_to_file()
718
0ed59edf 719
0415b981
RP
720
721/**
722 * @brief returns the current name of the log file.
723 * @return the name of the log file; empty if none was given.
724 *
725 * This function returns the last used log file name; even
726 * when logging to that file is currently disabled.
727 */
728std::string get_log_file_name()
729{
57924725
TJ
730 boost::recursive_mutex::scoped_lock lock(LoggerLock);
731
0415b981
RP
732 return g_log_file_name;
733} // eo get_log_file_name()
734
735
0ed59edf
RP
736/**
737 * @brief re-opens the logfiles (if applicable).
738 */
739void reopen()
740{
57924725
TJ
741 boost::recursive_mutex::scoped_lock lock(LoggerLock);
742
0ed59edf
RP
743 if (g_log_stream_ptr)
744 {
745 enable_log_file(false); // closes log, but holds the name.
746 enable_log_file(true); // opens the log file again.
747 }
748} // eo reopen()
749
750
b6fb82a2
RP
751/**
752 * set a new log level.
753 * @param level the new log level.
754 * @return the previous log level.
755 */
756int set_log_level(int level)
757{
57924725
TJ
758 boost::recursive_mutex::scoped_lock lock(LoggerLock);
759
a47d646e
TJ
760 int previous = g_max_level;
761
762 // Sanity check
763 if (level < LogLevel::Emergency)
764 level = LogLevel::Emergency;
765 else if (level > LogLevel::Debug)
766 level = LogLevel::Debug;
767
768 g_max_level = level;
769
770 return previous;
b6fb82a2
RP
771} // eo set_log_level(int)
772
773
774/**
775 * returns the current log level.
776 * @return the current log level.
777 */
778int get_log_level()
779{
57924725
TJ
780 boost::recursive_mutex::scoped_lock lock(LoggerLock);
781
f61aeac1 782 return g_max_level;
b6fb82a2
RP
783} // eo get_log_level()
784
785
786/**
787 * returns if the current log level covers the given level.
788 * This is a convenience function for optimization of log output (especially debug output).
789 * @param level the level which should be tested for.
790 * @return @a true iff a message with the level would be written out.
791 */
792bool has_log_level(int level)
793{
57924725
TJ
794 boost::recursive_mutex::scoped_lock lock(LoggerLock);
795
f61aeac1 796 return (g_max_level >= level);
b6fb82a2
RP
797} // eo has_log_level(int)
798
799
800
801} // eo namespace Logger
802} // eo namespace I2n