2 The software in this package is distributed under the GNU General
3 Public License version 2 (with a special exception described below).
5 A copy of GNU General Public License (GPL) is included in this distribution,
6 in the file COPYING.GPL.
8 As a special exception, if other files instantiate templates or use macros
9 or inline functions from this file, or you compile this file and link it
10 with other works to produce a work based on this file, this file
11 does not by itself cause the resulting work to be covered
12 by the GNU General Public License.
14 However the source code for this file must still be made available
15 in accordance with section (3) of the GNU General Public License.
17 This exception does not invalidate any other reasons why a work based
18 on this file might be covered by the GNU General Public License.
21 * @brief implementaton of logging functions.
23 * @copyright © Copyright 2007-2008 by Intra2net AG
25 * @note Don't call loggers from global constructed objects
26 * as this depends on the global object construction sequence.
29 #include "logfunc.hpp"
39 #include <boost/shared_ptr.hpp>
40 #include <boost/thread/recursive_mutex.hpp>
41 #include <boost/thread/thread.hpp>
43 #include <stringfunc.hxx>
44 #include <filefunc.hxx>
53 * @brief the global logger instance.
55 * This may be used in all cases wheer a specialized logger is not available
56 * or shouldn't be used for some reason.
58 PartLogger GlobalLogger("");
64 /// Global lock for the logger. Used during all syslog operations
65 /// and modification of our shared local variables.
66 static boost::recursive_mutex LoggerLock;
72 static std::string g_ident;
73 static Facility g_facility;
74 static bool g_syslog_opened = false;
75 static int g_max_level= LogLevel::Warning;
77 static bool g_stderr_log = false;
79 static std::string g_log_file_name;
80 static boost::shared_ptr< std::ofstream > g_log_stream_ptr;
83 * @brief lookup array for translating our log levels to syslog level.
85 static int loglevel_2_syslog_level[ LogLevel::_LogLevel_END ] = {
98 * @brief lookup array for translating our log levels to a short (4 character) tag.
100 * These tags are used when logs are written to stderr or into a log file.
102 static std::string loglevel_2_short_tag[ LogLevel::_LogLevel_END ] = {
115 * @brief a copy of the current identifier used for syslog.
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**!)
120 static char* syslog_ident= NULL;
128 * @brief close syslog.
132 boost::recursive_mutex::scoped_lock lock(LoggerLock);
142 g_syslog_opened= false;
144 } // eo close_syslog()
148 * @brief open syslog.
152 boost::recursive_mutex::scoped_lock lock(LoggerLock);
155 syslog_ident= strdup(g_ident.c_str());
156 openlog( syslog_ident, LOG_CONS|LOG_PID, g_facility);
157 g_syslog_opened= true;
158 } // eo open_syslog()
162 * @brief get syslog level from internal log level.
163 * @param level log level.
164 * @return syslog level
166 int get_syslog_level( int level )
170 if (level >=0 && level < LogLevel::_LogLevel_END)
172 return loglevel_2_syslog_level[level];
174 return (level<0) ? (LOG_EMERG) : (LOG_DEBUG);
175 } // eo get_syslog_level(int)
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.
183 std::string get_level_tag( int level )
187 if (level >=0 && level < LogLevel::_LogLevel_END)
189 return loglevel_2_short_tag[level];
191 return (level<0) ? loglevel_2_short_tag[0] : loglevel_2_short_tag[ LogLevel::_LogLevel_END -1 ];
192 } // eo get_level_tag(int)
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.
200 * Write the message to every enabled log channel.
202 * If syslog is enabled the message is passed unmodified to syslog.
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.
208 void log_msg( int level, const std::string& msg)
210 boost::recursive_mutex::scoped_lock lock(LoggerLock);
212 if (not g_syslog_opened and not g_stderr_log and not g_log_stream_ptr)
214 // if nothing is opened for logging: we activate syslog!
220 ::syslog( get_syslog_level(level), "%s", msg.c_str());
222 // file(/stream) logging:
223 if (g_stderr_log or g_log_stream_ptr) // add more log "enabled" expressions here...
225 // here we need must do something more with the msg...
229 std::ostringstream ostr;
230 // add time stamp (syslog like: "Mon DD HH:MM:SS") :
232 time_t t = time(NULL);
235 if (localtime_r(&t, &ta) == NULL)
236 memset(&ta, 0, sizeof(struct tm));
238 std::strftime(buffer, sizeof(buffer),"%b %d %H:%M:%S ", &ta);
241 ostr << get_level_tag(level) << " ";
242 ostr << g_ident << "[" << getpid() << "]: ";
247 std::string indent_string(prefix.size(), ' ');
248 std::list< std::string > parts;
249 split_string( chomp(msg,"\n"), parts, "\n");
250 std::ostringstream ostr;
252 for(std::list< std::string >::const_iterator it= parts.begin();
256 if (it != parts.begin())
258 ostr << indent_string;
260 ostr << *it << std::endl;
267 std::cerr << new_msg;
269 if (g_log_stream_ptr)
271 *g_log_stream_ptr << new_msg << std::flush;
278 * @brief "real" log function for part messages.
279 * @param level the log level.
280 * @param part the part(/module) name(/id)
281 * @param msg the log message.
283 * basically calls @a log(), but prepends the part (if not empty) in square brackets to the message.
287 const std::string& part,
288 const std::string& msg)
290 // Note: Locking is done in log_msg()
294 std::ostringstream ostr;
295 ostr << "[" << part << "] " << msg;
296 log_msg(level, ostr.str());
302 } // eo log_part_msg(int,const std::string&,const std::string&)
306 * @brief returns the name of the program (/binary)
307 * @return the program name if it could be determined; generated name else.
309 * Tries to determine the name of the binary.
311 * If no name could be determined, one is built.
313 std::string get_program_name()
318 // determine the program name:
320 // try to determine the name using the exe link:
321 std::string exe_path;
323 std::ostringstream ostr;
324 ostr << "/proc/" << ::getpid() << "/exe";
325 exe_path= ostr.str();
327 std::string binary_path= read_link(exe_path);
328 if (!binary_path.empty())
330 result= basename(binary_path);
335 // no program name found up to this point.
336 // make a name (as fallback solution):
337 std::ostringstream ostr;
338 ostr << "prg-" << ::getpid();
342 } // eo get_program_name
348 // Note: Locking is done in close_syslog();
350 //TODO other cleanups?
359 std::atexit( _cleanup );
364 } // eo namespace <anonymous>
369 ** implementation of Facility
373 const int Facility::AuthPriv= LOG_AUTH;
374 const int Facility::Cron = LOG_CRON;
375 const int Facility::Daemon = LOG_DAEMON;
376 const int Facility::Kern = LOG_KERN;
377 const int Facility::Mail = LOG_MAIL;
378 const int Facility::News = LOG_NEWS;
379 const int Facility::Syslog = LOG_SYSLOG;
380 const int Facility::User = LOG_USER;
381 const int Facility::UUCP = LOG_UUCP;
382 const int Facility::Local0 = LOG_LOCAL0;
383 const int Facility::Local1 = LOG_LOCAL1;
384 const int Facility::Local2 = LOG_LOCAL2;
385 const int Facility::Local3 = LOG_LOCAL3;
386 const int Facility::Local4 = LOG_LOCAL4;
387 const int Facility::Local5 = LOG_LOCAL5;
388 const int Facility::Local6 = LOG_LOCAL6;
389 const int Facility::Local7 = LOG_LOCAL7;
394 ** implementation of PartLogger::LogHelper
397 PartLogger::LogHelper::LogHelper(PartLogger& logger, int level, const SourceLocation& loc)
402 StreamPtr.reset(new std::ostringstream());
403 } // eo PartLogger::LogHelper::LogHelper(PartLogger&,int)
405 PartLogger::LogHelper::LogHelper(const LogHelper& helper)
406 : Logger(helper.Logger)
407 , Level(helper.Level)
408 , Location(helper.Location)
409 , StreamPtr(helper.StreamPtr)
411 } // eo PartLogger::LogHelper::LogHelper(const LogHelper&)
414 PartLogger::LogHelper::~LogHelper()
420 //*m_stream_ptr << " at " << m_loc.Line << " in " << m_loc.FunctionName;
421 *StreamPtr << " @" << Location.get_location_tag();
423 std::string msg(StreamPtr->str());
426 Logger.log(Level,msg);
429 } // eo PartLogger::LogHelper::~LogHelper
433 ** implementation of PartLogger
437 * constructor for a part logger.
438 * @param part name of the part (module name) using the logger instance.
440 PartLogger::PartLogger(const std::string& part)
443 } // eo PartLogger::PartLogger(const std.:string&)
447 * @brief constructor for a part logger at module level.
448 * @param loc the source location where the PartLogger is constructed.
450 * The part name is derived from the filename given with the source location by
451 * using the basename and cutting off the C++ file suffix (if it is a well known one;
452 * currently known extensions: cpp, cxx, c++, cc, C).
454 PartLogger::PartLogger( const SourceLocation& loc )
456 if (loc.Line>0 && ! loc.File.empty())
458 std::string str= basename(loc.File);
459 Part= remove_suffix(str,".cpp");
460 if (Part == str) Part= remove_suffix(str,".cxx");
461 if (Part == str) Part= remove_suffix(str,".c++");
462 if (Part == str) Part= remove_suffix(str,".cc");
463 if (Part == str) Part= remove_suffix(str,".C");
469 }// PartLogger::PartLogger(const SourceLocation&)
472 PartLogger::~PartLogger()
478 * generic log function.
479 * @param level the log level.
480 * @param msg the log message.
482 void PartLogger::log(int level, const std::string &msg)
484 boost::recursive_mutex::scoped_lock lock(LoggerLock);
486 if (level <= g_max_level)
488 log_part_msg(level, Part, msg);
490 } // eo PartLogger::log(int,const std::string);
493 void PartLogger::fatal(const std::string& msg)
496 } // eo PartLogger::fatal(const std::string&)
499 void PartLogger::alert(const std::string& msg)
502 } // eo PartLogger::alert(const std::string&)
505 void PartLogger::critical(const std::string& msg)
508 } // eo PartLogger::critical(const std::string&)
511 void PartLogger::error(const std::string& msg)
514 } // eo PartLogger::error(const std::string&)
517 void PartLogger::warning(const std::string& msg)
519 log(LOG_WARNING, msg);
520 } // eo PartLogger::warning(const std::string&)
523 void PartLogger::notice(const std::string& msg)
525 log(LOG_NOTICE, msg);
526 } // eo PartLogger::notice(const std::string&)
529 void PartLogger::info(const std::string& msg)
532 } // eo PartLogger::info(const std::string&)
535 void PartLogger::debug(const std::string& msg)
538 } // eo PartLogger::debug(const std::string&)
542 PartLogger::LogHelper PartLogger::fatal(const SourceLocation& loc)
544 return PartLogger::LogHelper(*this,LOG_EMERG,loc);
545 } // eo PartLogger::fatal(const SourceLocation&)
548 PartLogger::LogHelper PartLogger::alert(const SourceLocation& loc)
550 return PartLogger::LogHelper(*this,LOG_ALERT,loc);
551 } // eo PartLogger::alert(const SourceLocation&)
554 PartLogger::LogHelper PartLogger::critical(const SourceLocation& loc)
556 return PartLogger::LogHelper(*this,LOG_CRIT,loc);
557 } // eo PartLogger::critical(const SourceLocation&)
560 PartLogger::LogHelper PartLogger::error(const SourceLocation& loc)
562 return PartLogger::LogHelper(*this,LOG_ERR,loc);
563 } // eo PartLogger::error(const SourceLocation&)
566 PartLogger::LogHelper PartLogger::warning(const SourceLocation& loc)
568 return PartLogger::LogHelper(*this,LOG_WARNING,loc);
569 } // eo PartLogger::warning(const SourceLocation&)
572 PartLogger::LogHelper PartLogger::notice(const SourceLocation& loc)
574 return PartLogger::LogHelper(*this,LOG_NOTICE,loc);
575 } // eo PartLogger::notice(const SourceLocation&)
578 PartLogger::LogHelper PartLogger::info(const SourceLocation& loc)
580 return PartLogger::LogHelper(*this,LOG_INFO,loc);
581 } // eo PartLogger::info(const SourceLocation&)
584 PartLogger::LogHelper PartLogger::debug(const SourceLocation& loc)
586 return PartLogger::LogHelper(*this,LOG_DEBUG,loc);
587 } // eo PartLogger::debug(const SourceLocation&)
595 * enable logging to syslog with a name and a facility.
596 * @param name the name used as ident.
597 * @param facility the facility which should be used.
599 void enable_syslog( const std::string& name, Facility facility )
601 boost::recursive_mutex::scoped_lock lock(LoggerLock);
605 g_facility= facility;
607 } // eo enable_syslog(const std::string,Facility)
611 * enable logging to syslog with a facility.
612 * The ident is used from a previous call or (if none was set) is
613 * determined by reading the program path from /proc/\<pid\>/exe.
614 * @param facility the facility which should be used.
616 void enable_syslog( Facility facility )
618 boost::recursive_mutex::scoped_lock lock(LoggerLock);
622 g_ident= get_program_name();
625 g_facility = facility;
627 } // eo enable_syslog(Facility)
631 * enable or disable logging to syslog.
632 * @param enable whether the logging to syslog should be enabled or not.
634 void enable_syslog( bool enable )
636 boost::recursive_mutex::scoped_lock lock(LoggerLock);
640 if (!g_syslog_opened)
642 enable_syslog( g_facility );
649 } // eo enable_syslog(bool)
653 * enable/ disable loggin to stderr.
654 * @param enable whether to enable or disable logging to stderr.
656 void enable_stderr_log(bool enable)
658 boost::recursive_mutex::scoped_lock lock(LoggerLock);
660 g_stderr_log= enable;
661 } // eo enableStderr;
666 * enable logging to a file.
667 * @param name path to the file.
669 * @note only one log file can be use at a time.
671 void enable_log_file( const std::string& name )
673 boost::recursive_mutex::scoped_lock lock(LoggerLock);
675 g_log_file_name= name;
676 g_log_stream_ptr.reset( new std::ofstream() );
677 g_log_stream_ptr->open( name.c_str(), std::ios::out|std::ios::app );
678 //std::cerr << "### opened \"" << name << "\"" << g_log_stream_ptr->good() << std::endl;
679 } // eo enable_log_file(const std::string&)
683 * enable or disable loggin to a file.
684 * if a logfile was already set by a previous call to enable_log_file(const std::string&)
685 * that one is used; else it logs to <tt>/var/log/</tt><em>program name</em><tt>.log</tt>.
686 * @param enable whether to enable or disable logging to a file.
688 void enable_log_file( bool enable )
690 boost::recursive_mutex::scoped_lock lock(LoggerLock);
694 if (g_log_file_name.empty())
696 std::ostringstream ostr;
697 ostr << "/var/log/" << get_program_name() << ".log";
698 enable_log_file( ostr.str() );
702 enable_log_file( g_log_file_name );
707 g_log_stream_ptr.reset();
709 } // eo enable_log_file(bool)
713 * @brief returns if loging to file is enabled and active.
714 * @return @a true if logfile is enabled and opened.
716 bool is_logging_to_file()
718 boost::recursive_mutex::scoped_lock lock(LoggerLock);
720 return g_log_stream_ptr and g_log_stream_ptr->good();
721 } // eo is_logging_to_file()
726 * @brief returns the current name of the log file.
727 * @return the name of the log file; empty if none was given.
729 * This function returns the last used log file name; even
730 * when logging to that file is currently disabled.
732 std::string get_log_file_name()
734 boost::recursive_mutex::scoped_lock lock(LoggerLock);
736 return g_log_file_name;
737 } // eo get_log_file_name()
741 * @brief re-opens the logfiles (if applicable).
745 boost::recursive_mutex::scoped_lock lock(LoggerLock);
747 if (g_log_stream_ptr)
749 enable_log_file(false); // closes log, but holds the name.
750 enable_log_file(true); // opens the log file again.
756 * set a new log level.
757 * @param level the new log level.
758 * @return the previous log level.
760 int set_log_level(int level)
762 boost::recursive_mutex::scoped_lock lock(LoggerLock);
764 int previous = g_max_level;
767 if (level < LogLevel::Emergency)
768 level = LogLevel::Emergency;
769 else if (level > LogLevel::Debug)
770 level = LogLevel::Debug;
775 } // eo set_log_level(int)
779 * returns the current log level.
780 * @return the current log level.
784 boost::recursive_mutex::scoped_lock lock(LoggerLock);
787 } // eo get_log_level()
791 * returns if the current log level covers the given level.
792 * This is a convenience function for optimization of log output (especially debug output).
793 * @param level the level which should be tested for.
794 * @return @a true iff a message with the level would be written out.
796 bool has_log_level(int level)
798 boost::recursive_mutex::scoped_lock lock(LoggerLock);
800 return (g_max_level >= level);
801 } // eo has_log_level(int)
805 } // eo namespace Logger
806 } // eo namespace I2n