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 This module is not thread safe!
26 * @todo make this module thread safe (only useful when we can use threads...)
29 #include "logfunc.hpp"
39 #include <boost/shared_ptr.hpp>
41 #include <stringfunc.hxx>
42 #include <filefunc.hxx>
52 * @brief the global logger instance.
54 * This may be used in all cases wheer a specialized logger is not available
55 * or shouldn't be used for some reason.
57 PartLogger GlobalLogger("");
69 bool g_syslog_opened = false;
70 int g_max_level= LogLevel::Warning;
72 bool g_stderr_log = false;
74 std::string g_log_file_name;
75 boost::shared_ptr< std::ofstream > g_log_stream_ptr;
79 * @brief lookup array for translating our log levels to syslog level.
81 int loglevel_2_syslog_level[ LogLevel::_LogLevel_END ] = {
94 * @brief lookup array for translating our log levels to a short (4 character) tag.
96 * These tags are used when logs are written to stderr or into a log file.
98 std::string loglevel_2_short_tag[ LogLevel::_LogLevel_END ] = {
111 * @brief a copy of the current identifier used for syslog.
113 * Keeping a copy of this ident is necessary since openlog doen't copy it's first
114 * argument but copies only the pointer! (what a **censored**!)
116 char* syslog_ident= NULL;
124 * @brief close syslog.
136 g_syslog_opened= false;
138 } // eo close_syslog()
142 * @brief open syslog.
147 syslog_ident= strdup(g_ident.c_str());
148 openlog( syslog_ident, LOG_CONS|LOG_PID, g_facility);
149 g_syslog_opened= true;
150 } // eo open_syslog()
154 * @brief get syslog level from internal log level.
155 * @param level log level.
156 * @return syslog level
158 int get_syslog_level( int level )
160 if (level >=0 && level < LogLevel::_LogLevel_END)
162 return loglevel_2_syslog_level[level];
164 return (level<0) ? (LOG_EMERG) : (LOG_DEBUG);
165 } // eo get_syslog_level(int)
169 * @brief get a short tag for the log level (/ message type)
170 * @param level the log level
171 * @return a short tag for the level.
173 std::string get_level_tag( int level )
175 if (level >=0 && level < LogLevel::_LogLevel_END)
177 return loglevel_2_short_tag[level];
179 return (level<0) ? loglevel_2_short_tag[0] : loglevel_2_short_tag[ LogLevel::_LogLevel_END -1 ];
180 } // eo get_level_tag(int)
184 * @brief the "real" log function which logs a message at a given level.
185 * @param level the log level to log the message at.
186 * @param msg the message.
188 * Write the message to every enabled log channel.
190 * If syslog is enabled the message is passed unmodified to syslog.
192 * If a stream log is enabled (stderr or file) then the message is prepended with date, time
193 * and process information (like syslog does). The message is splitted at line ends and
194 * consecutive lines are indented.
196 void log_msg( int level, const std::string& msg)
198 if (not g_syslog_opened and not g_stderr_log and not g_log_stream_ptr)
200 // if nothing is opened for logging: we activate syslog!
206 ::syslog( get_syslog_level(level), "%s", msg.c_str());
208 // file(/stream) logging:
209 if (g_stderr_log or g_log_stream_ptr) // add more log "enabled" expressions here...
211 // here we need must do something more with the msg...
215 std::ostringstream ostr;
216 // add time stamp (syslog like: "Mon DD HH:MM:SS") :
218 time_t t = time(NULL);
220 std::strftime(buffer, sizeof(buffer),"%b %d %H:%M:%S ", std::localtime(&t));
223 ostr << get_level_tag(level) << " ";
224 ostr << g_ident << "[" << getpid() << "]: ";
229 std::string indent_string(prefix.size(), ' ');
230 std::list< std::string > parts;
231 split_string( chomp(msg,"\n"), parts, "\n");
232 std::ostringstream ostr;
234 for(std::list< std::string >::const_iterator it= parts.begin();
238 if (it != parts.begin())
240 ostr << indent_string;
242 ostr << *it << std::endl;
249 std::cerr << new_msg;
251 if (g_log_stream_ptr)
253 *g_log_stream_ptr << new_msg << std::flush;
260 * @brief "real" log function for part messages.
261 * @param level the log level.
262 * @param part the part(/module) name(/id)
263 * @param msg the log message.
265 * basically calls @a log(), but prepends the part (if not empty) in square brackets to the message.
269 const std::string& part,
270 const std::string& msg)
274 std::ostringstream ostr;
275 ostr << "[" << part << "] " << msg;
276 log_msg(level, ostr.str());
282 } // eo log_part_msg(int,const std::string&,const std::string&)
286 * @brief returns the name of the program (/binary)
287 * @return the program name if it could be determined; generated name else.
289 * Tries to determine the name of the binary.
291 * If no name could be determined, one is built.
293 std::string get_program_name()
296 // determine the program name:
298 // try to determine the name using the exe link:
299 std::string exe_path;
301 std::ostringstream ostr;
302 ostr << "/proc/" << ::getpid() << "/exe";
303 exe_path= ostr.str();
305 std::string binary_path= read_link(exe_path);
306 if (!binary_path.empty())
308 result= basename(binary_path);
313 // no program name found up to this point.
314 // make a name (as fallback solution):
315 std::ostringstream ostr;
316 ostr << "prg-" << ::getpid();
320 } // eo get_program_name
327 //TODO other cleanups?
336 std::atexit( _cleanup );
341 } // eo namespace <anonymous>
346 ** implementation of Facility
350 const int Facility::AuthPriv= LOG_AUTH;
351 const int Facility::Cron = LOG_CRON;
352 const int Facility::Daemon = LOG_DAEMON;
353 const int Facility::Kern = LOG_KERN;
354 const int Facility::Mail = LOG_MAIL;
355 const int Facility::News = LOG_NEWS;
356 const int Facility::Syslog = LOG_SYSLOG;
357 const int Facility::User = LOG_USER;
358 const int Facility::UUCP = LOG_UUCP;
359 const int Facility::Local0 = LOG_LOCAL0;
360 const int Facility::Local1 = LOG_LOCAL1;
361 const int Facility::Local2 = LOG_LOCAL2;
362 const int Facility::Local3 = LOG_LOCAL3;
363 const int Facility::Local4 = LOG_LOCAL4;
364 const int Facility::Local5 = LOG_LOCAL5;
365 const int Facility::Local6 = LOG_LOCAL6;
366 const int Facility::Local7 = LOG_LOCAL7;
371 ** implementation of PartLogger::LogHelper
374 PartLogger::LogHelper::LogHelper(PartLogger& logger, int level, const SourceLocation& loc)
379 StreamPtr.reset(new std::ostringstream());
380 } // eo PartLogger::LogHelper::LogHelper(PartLogger&,int)
382 PartLogger::LogHelper::LogHelper(const LogHelper& helper)
383 : Logger(helper.Logger)
384 , Level(helper.Level)
385 , Location(helper.Location)
386 , StreamPtr(helper.StreamPtr)
388 } // eo PartLogger::LogHelper::LogHelper(const LogHelper&)
391 PartLogger::LogHelper::~LogHelper()
397 //*m_stream_ptr << " at " << m_loc.Line << " in " << m_loc.FunctionName;
398 *StreamPtr << " @" << Location.get_location_tag();
400 std::string msg(StreamPtr->str());
403 Logger.log(Level,msg);
406 } // eo PartLogger::LogHelper::~LogHelper
410 ** implementation of PartLogger
414 * constructor for a part logger.
415 * @param part name of the part (module name) using the logger instance.
417 PartLogger::PartLogger(const std::string& part)
420 } // eo PartLogger::PartLogger(const std.:string&)
424 * @brief constructor for a part logger at module level.
425 * @param loc the source location where the PartLogger is constructed.
427 * The part name is derived from the filename given with the source location by
428 * using the basename and cutting off the C++ file suffix (if it is a well known one;
429 * currently known extensions: cpp, cxx, c++, cc, C).
431 PartLogger::PartLogger( const SourceLocation& loc )
433 if (loc.Line>0 && ! loc.File.empty())
435 std::string str= basename(loc.File);
436 Part= remove_suffix(str,".cpp");
437 if (Part == str) Part= remove_suffix(str,".cxx");
438 if (Part == str) Part= remove_suffix(str,".c++");
439 if (Part == str) Part= remove_suffix(str,".cc");
440 if (Part == str) Part= remove_suffix(str,".C");
446 }// PartLogger::PartLogger(const SourceLocation&)
449 PartLogger::~PartLogger()
455 * generic log function.
456 * @param level the log level.
457 * @param msg the log message.
459 void PartLogger::log(int level, const std::string msg)
461 if (level <= g_max_level)
463 log_part_msg(level, Part, msg);
465 } // eo PartLogger::log(int,const std::string);
468 void PartLogger::fatal(const std::string& msg)
471 } // eo PartLogger::fatal(const std::string&)
474 void PartLogger::alert(const std::string& msg)
477 } // eo PartLogger::alert(const std::string&)
480 void PartLogger::critical(const std::string& msg)
483 } // eo PartLogger::critical(const std::string&)
486 void PartLogger::error(const std::string& msg)
489 } // eo PartLogger::error(const std::string&)
492 void PartLogger::warning(const std::string& msg)
494 log(LOG_WARNING, msg);
495 } // eo PartLogger::warning(const std::string&)
498 void PartLogger::notice(const std::string& msg)
500 log(LOG_NOTICE, msg);
501 } // eo PartLogger::notice(const std::string&)
504 void PartLogger::info(const std::string& msg)
507 } // eo PartLogger::info(const std::string&)
510 void PartLogger::debug(const std::string& msg)
513 } // eo PartLogger::debug(const std::string&)
517 PartLogger::LogHelper PartLogger::fatal(const SourceLocation& loc)
519 return PartLogger::LogHelper(*this,LOG_EMERG,loc);
520 } // eo PartLogger::fatal(const SourceLocation&)
523 PartLogger::LogHelper PartLogger::alert(const SourceLocation& loc)
525 return PartLogger::LogHelper(*this,LOG_ALERT,loc);
526 } // eo PartLogger::alert(const SourceLocation&)
529 PartLogger::LogHelper PartLogger::critical(const SourceLocation& loc)
531 return PartLogger::LogHelper(*this,LOG_CRIT,loc);
532 } // eo PartLogger::critical(const SourceLocation&)
535 PartLogger::LogHelper PartLogger::error(const SourceLocation& loc)
537 return PartLogger::LogHelper(*this,LOG_ERR,loc);
538 } // eo PartLogger::error(const SourceLocation&)
541 PartLogger::LogHelper PartLogger::warning(const SourceLocation& loc)
543 return PartLogger::LogHelper(*this,LOG_WARNING,loc);
544 } // eo PartLogger::warning(const SourceLocation&)
547 PartLogger::LogHelper PartLogger::notice(const SourceLocation& loc)
549 return PartLogger::LogHelper(*this,LOG_NOTICE,loc);
550 } // eo PartLogger::notice(const SourceLocation&)
553 PartLogger::LogHelper PartLogger::info(const SourceLocation& loc)
555 return PartLogger::LogHelper(*this,LOG_INFO,loc);
556 } // eo PartLogger::info(const SourceLocation&)
559 PartLogger::LogHelper PartLogger::debug(const SourceLocation& loc)
561 return PartLogger::LogHelper(*this,LOG_DEBUG,loc);
562 } // eo PartLogger::debug(const SourceLocation&)
570 * enable logging to syslog with a name and a facility.
571 * @param name the name used as ident.
572 * @param facility the facility which should be used.
574 void enable_syslog( const std::string& name, Facility facility )
578 g_facility= facility;
580 } // eo enable_syslog(const std::string,Facility)
584 * enable logging to syslog with a facility.
585 * The ident is used from a previous call or (if none was set) is
586 * determined by reading the program path from /proc/\<pid\>/exe.
587 * @param facility the facility which should be used.
589 void enable_syslog( Facility facility )
593 g_ident= get_program_name();
596 g_facility = facility;
598 } // eo enable_syslog(Facility)
602 * enable or disable logging to syslog.
603 * @param enable whether the logging to syslog should be enabled or not.
605 void enable_syslog( bool enable )
609 if (!g_syslog_opened)
611 enable_syslog( g_facility );
618 } // eo enable_syslog(bool)
622 * enable/ disable loggin to stderr.
623 * @param enable whether to enable or disable logging to stderr.
625 void enable_stderr_log(bool enable)
627 g_stderr_log= enable;
628 } // eo enableStderr;
633 * enable logging to a file.
634 * @param name path to the file.
636 * @note only one log file can be use at a time.
638 void enable_log_file( const std::string& name )
640 g_log_file_name= name;
641 g_log_stream_ptr.reset( new std::ofstream() );
642 g_log_stream_ptr->open( name.c_str(), std::ios::out|std::ios::app );
643 //std::cerr << "### opened \"" << name << "\"" << g_log_stream_ptr->good() << std::endl;
644 } // eo enable_log_file(const std::string&)
648 * enable or disable loggin to a file.
649 * if a logfile was already set by a previous call to enable_log_file(const std::string&)
650 * that one is used; else it logs to <tt>/var/log/</tt><em>program name</em><tt>.log</tt>.
651 * @param enable whether to enable or disable logging to a file.
653 void enable_log_file( bool enable )
657 if (g_log_file_name.empty())
659 std::ostringstream ostr;
660 ostr << "/var/log/" << get_program_name() << ".log";
661 enable_log_file( ostr.str() );
665 enable_log_file( g_log_file_name );
670 g_log_stream_ptr.reset();
672 } // eo enable_log_file(bool)
676 * @brief returns if loging to file is enabled and active.
677 * @return @a true if logfile is enabled and opened.
679 bool is_logging_to_file()
681 return g_log_stream_ptr and g_log_stream_ptr->good();
682 } // eo is_logging_to_file()
687 * @brief returns the current name of the log file.
688 * @return the name of the log file; empty if none was given.
690 * This function returns the last used log file name; even
691 * when logging to that file is currently disabled.
693 std::string get_log_file_name()
695 return g_log_file_name;
696 } // eo get_log_file_name()
700 * @brief re-opens the logfiles (if applicable).
704 if (g_log_stream_ptr)
706 enable_log_file(false); // closes log, but holds the name.
707 enable_log_file(true); // opens the log file again.
713 * set a new log level.
714 * @param level the new log level.
715 * @return the previous log level.
717 int set_log_level(int level)
719 int previous = g_max_level;
722 if (level < LogLevel::Emergency)
723 level = LogLevel::Emergency;
724 else if (level > LogLevel::Debug)
725 level = LogLevel::Debug;
730 } // eo set_log_level(int)
734 * returns the current log level.
735 * @return the current log level.
740 } // eo get_log_level()
744 * returns if the current log level covers the given level.
745 * This is a convenience function for optimization of log output (especially debug output).
746 * @param level the level which should be tested for.
747 * @return @a true iff a message with the level would be written out.
749 bool has_log_level(int level)
751 return (g_max_level >= level);
752 } // eo has_log_level(int)
756 } // eo namespace Logger
757 } // eo namespace I2n