From b6fb82a25d4deca66349bf68267fed80145b6672 Mon Sep 17 00:00:00 2001 From: Reinhard Pfau Date: Fri, 11 Apr 2008 08:53:25 +0000 Subject: [PATCH] libi2ncommon: (reinhard) integrated "logfunc" and "tracing" from connd. --- src/Makefile.am | 9 +- src/log_macros.hpp | 42 +++ src/logfunc.cpp | 662 +++++++++++++++++++++++++++++++++++++++++++ src/logfunc.hpp | 209 ++++++++++++++ src/source_track_basics.cpp | 75 +++++ src/source_track_basics.hpp | 68 +++++ src/tracefunc.cpp | 163 +++++++++++ src/tracefunc.hpp | 63 ++++ test/Makefile.am | 2 +- test/test_logging.cpp | 132 +++++++++ 10 files changed, 1420 insertions(+), 5 deletions(-) create mode 100644 src/log_macros.hpp create mode 100644 src/logfunc.cpp create mode 100644 src/logfunc.hpp create mode 100644 src/source_track_basics.cpp create mode 100644 src/source_track_basics.hpp create mode 100644 src/tracefunc.cpp create mode 100644 src/tracefunc.hpp create mode 100644 test/test_logging.cpp diff --git a/src/Makefile.am b/src/Makefile.am index ced5609..902807b 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -5,11 +5,12 @@ INCLUDES = -I$(top_srcdir)/src @LIBGETTEXT_CFLAGS@ @LIBICONV_CFLAGS@ $(all_inclu # the library search path. lib_LTLIBRARIES = libi2ncommon.la include_HEADERS = containerfunc.hpp daemonfunc.hpp filefunc.hxx \ - insocketstream.hxx ip_type.hxx ipfunc.hxx logread.hxx oftmpstream.hxx pidfile.hpp \ - pipestream.hxx stringfunc.hxx timefunc.hxx userfunc.hpp + insocketstream.hxx ip_type.hxx ipfunc.hxx log_macros.hpp logfunc.hpp logread.hxx \ + oftmpstream.hxx pidfile.hpp pipestream.hxx source_track_basics.hpp stringfunc.hxx \ + timefunc.hxx tracefunc.hpp userfunc.hpp libi2ncommon_la_SOURCES = containerfunc.cpp daemonfunc.cpp filefunc.cpp \ - ipfunc.cpp logread.cpp oftmpstream.cpp pidfile.cpp stringfunc.cpp timefunc.cpp \ - userfunc.cpp + ipfunc.cpp logfunc.cpp logread.cpp oftmpstream.cpp pidfile.cpp \ + source_track_basics.cpp stringfunc.cpp timefunc.cpp tracefunc.cpp userfunc.cpp # Note: If you specify a:b:c as the version in the next line, # the library that is made has version (a-c).c.b. In this diff --git a/src/log_macros.hpp b/src/log_macros.hpp new file mode 100644 index 0000000..931990f --- /dev/null +++ b/src/log_macros.hpp @@ -0,0 +1,42 @@ +/** @file + * @brief macros to ease some common logging cases. + * + * The log macros are put into a file separate from the (base) logging files since + * every coder can decide if he/she want's to use these macros or not. + * (Not everyone like macros...) + * + * + * @author Reinhard Pfau \ + * + * @copyright © Copyright 2008 by Intra2net AG + * @license commercial + * + * info@intra2net.com + * + */ + +#ifndef __I2N_COMMON_LOG_MACROS_HPP__ +#define __I2N_COMMON_LOG_MACROS_HPP__ + +#include "logfunc.hpp" + + +/* +** +*/ + +/** + * @brief to be inserted in prepared but unimplemented functions. + */ +#define UNIMPLEMENTED() do{ ::I2n::Logger::GlobalLogger.warning(HERE) << "not implemented"; }while(false) + + +/** + * @brief to be inserted in places which should be never reached during normal program execution. + */ +#define NOT_REACHED() do{ ::I2n::Logger::GlobalLogger.alert(HERE) << "this part should not be reached"; }while(false) + + + + +#endif diff --git a/src/logfunc.cpp b/src/logfunc.cpp new file mode 100644 index 0000000..2f40ae6 --- /dev/null +++ b/src/logfunc.cpp @@ -0,0 +1,662 @@ +/** @file + * @brief implementaton of logging functions. + * + * @copyright © Copyright 2007-2008 by Intra2net AG + * @license commercial + * + * info@intra2net.com + * + * @note This module is not thread safe! + * @todo make this module thread safe (only useful when we can use threads...) + */ + +#include "logfunc.hpp" + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + + +namespace I2n +{ +namespace Logger +{ + + +/** + * @brief the global logger instance. + * + * This may be used in all cases wheer a specialized logger is not available + * or shouldn't be used for some reason. + */ +PartLogger GlobalLogger(""); + + +namespace +{ + +/* +** local globals: +*/ + +std::string g_ident; +Facility g_facility; +bool g_syslog_opened = false; +int g_max_level= LogLevel::Warning; + +bool g_stderr_log = false; + +std::string g_log_file_name; +boost::shared_ptr< std::ofstream > g_log_stream_ptr; + + +/** + * @brief lookup array for translating our log levels to syslog level. + */ +int loglevel_2_syslog_level[ LogLevel::_LogLevel_END ] = { + LOG_EMERG, + LOG_ALERT, + LOG_CRIT, + LOG_ERR, + LOG_WARNING, + LOG_NOTICE, + LOG_INFO, + LOG_DEBUG +}; + + +/** + * @brief lookup array for translating our log levels to a short (4 character) tag. + * + * These tags are used when logs are written to stderr or into a log file. + */ +std::string loglevel_2_short_tag[ LogLevel::_LogLevel_END ] = { + "EMRG", + "ALRT", + "CRIT", + "ERR ", + "WARN", + "NOTE", + "INFO", + "DBUG" +}; + + +/** + * @brief a copy of the current identifier used for syslog. + * + * Keeping a copy of this ident is necessary since openlog doen't copy it's first + * argument but copies only the pointer! (what a **censored**!) + */ +char* syslog_ident= NULL; + + +/* +** functions: +*/ + +/** + * @brief close syslog. + */ +void close_syslog() +{ + if (g_syslog_opened) + { + closelog(); + if (syslog_ident) + { + free(syslog_ident); + syslog_ident= NULL; + } + g_syslog_opened= false; + } +} // eo close_syslog() + + +/** + * @brief open syslog. + */ +void open_syslog() +{ + close_syslog(); + syslog_ident= strdup(g_ident.c_str()); + openlog( syslog_ident, LOG_CONS|LOG_PID, g_facility); + g_syslog_opened= true; +} // eo open_syslog() + + +/** + * @brief get syslog level from internal log level. + * @param level log level. + * @return syslog level + */ +int get_syslog_level( int level ) +{ + if (level >=0 && level <= LogLevel::_LogLevel_END) + { + return loglevel_2_syslog_level[level]; + } + return (level<0) ? (LOG_EMERG) : (LOG_DEBUG); +} // eo get_syslog_level(int) + + +/** + * @brief get a short tag for the log level (/ message type) + * @param level the log level + * @return a short tag for the level. + */ +std::string get_level_tag( int level ) +{ + if (level >=0 && level <= LogLevel::_LogLevel_END) + { + return loglevel_2_short_tag[level]; + } + return (level<0) ? loglevel_2_short_tag[0] : loglevel_2_short_tag[ LogLevel::_LogLevel_END -1 ]; +} // eo get_level_tag(int) + + +/** + * @brief the "real" log function which logs a message at a given level. + * @param level the log level to log the message at. + * @param msg the message. + * + * Write the message to every enabled log channel. + * + * If syslog is enabled the message is passed unmodified to syslog. + * + * If a stream log is enabled (stderr or file) then the message is prepended with date, time + * and process information (like syslog does). The message is splitted at line ends and + * consecutive lines are indented. + */ +void log_msg( int level, const std::string& msg) +{ + if (not g_syslog_opened and not g_stderr_log and not g_log_stream_ptr) + { + // if nothing is opened for logging: we activate syslog! + enable_syslog(true); + } + + if (g_syslog_opened) + { + ::syslog( get_syslog_level(level), "%s", msg.c_str()); + } + // file(/stream) logging: + if (g_stderr_log or g_log_stream_ptr) // add more log "enabled" expressions here... + { + // here we need must do something more with the msg... + std::string new_msg; + std::string prefix; + { + std::ostringstream ostr; + // add time stamp (syslog like: "Mon DD HH:MM:SS") : + { + time_t t = time(NULL); + char buffer[32]; + std::strftime(buffer, sizeof(buffer),"%b %d %H:%M:%S ", std::localtime(&t)); + ostr << buffer; + } + ostr << get_level_tag(level) << " "; + ostr << g_ident << "[" << getpid() << "]: "; + prefix= ostr.str(); + } + { + { + std::string indent_string(prefix.size(), ' '); + std::list< std::string > parts; + split_string( chomp(msg,"\n"), parts, "\n"); + std::ostringstream ostr; + ostr << prefix; + for(std::list< std::string >::const_iterator it= parts.begin(); + it != parts.end(); + ++it) + { + if (it != parts.begin()) + { + ostr << indent_string; + } + ostr << *it << std::endl; + } + new_msg= ostr.str(); + } + } + if (g_stderr_log) + { + std::cerr << new_msg; + } + if (g_log_stream_ptr) + { + *g_log_stream_ptr << new_msg << std::flush; + } + } +} // eo log_msg + + +/** + * @brief "real" log function for part messages. + * @param level the log level. + * @param part the part(/module) name(/id) + * @param msg the log message. + * + * basically calls @a log(), but prepends the part (if not empty) in square brackets to the message. + */ +void log_part_msg( + int level, + const std::string& part, + const std::string& msg) +{ + if (!part.empty()) + { + std::ostringstream ostr; + ostr << "[" << part << "] " << msg; + log_msg(level, ostr.str()); + } + else + { + log_msg(level, msg); + } +} // eo log_part_msg(int,const std::string&,const std::string&) + + + +void _cleanup() +{ + close_syslog(); + //TODO other cleanups? +} // _cleanup + + +class __Initializer +{ + public: + __Initializer() + { + std::atexit( _cleanup ); + } +} __initialize; + + +} // eo namespace + + + +/* +** implementation of Facility +*/ + + +const int Facility::AuthPriv= LOG_AUTH; +const int Facility::Cron = LOG_CRON; +const int Facility::Daemon = LOG_DAEMON; +const int Facility::Kern = LOG_KERN; +const int Facility::Mail = LOG_MAIL; +const int Facility::News = LOG_NEWS; +const int Facility::Syslog = LOG_SYSLOG; +const int Facility::User = LOG_USER; +const int Facility::UUCP = LOG_UUCP; +const int Facility::Local0 = LOG_LOCAL0; +const int Facility::Local1 = LOG_LOCAL1; +const int Facility::Local2 = LOG_LOCAL2; +const int Facility::Local3 = LOG_LOCAL3; +const int Facility::Local4 = LOG_LOCAL4; +const int Facility::Local5 = LOG_LOCAL5; +const int Facility::Local6 = LOG_LOCAL6; +const int Facility::Local7 = LOG_LOCAL7; + + + +/* +** implementation of PartLogger::LogHelper +*/ + +PartLogger::LogHelper::LogHelper(PartLogger& logger, int level, const SourceLocation& loc) +: Logger(logger) +, Level(level) +, Location(loc) +{ + StreamPtr.reset(new std::ostringstream()); +} // eo PartLogger::LogHelper::LogHelper(PartLogger&,int) + +PartLogger::LogHelper::LogHelper(const LogHelper& helper) +: Logger(helper.Logger) +, Level(helper.Level) +, Location(helper.Location) +, StreamPtr(helper.StreamPtr) +{ +} // eo PartLogger::LogHelper::LogHelper(const LogHelper&) + + +PartLogger::LogHelper::~LogHelper() +{ + if (StreamPtr.get()) + { + if (Location) + { + //*m_stream_ptr << " at " << m_loc.Line << " in " << m_loc.FunctionName; + *StreamPtr << " @" << Location.get_location_tag(); + } + std::string msg(StreamPtr->str()); + if (!msg.empty()) + { + Logger.log(Level,msg); + } + } +} // eo PartLogger::LogHelper::~LogHelper + + +/* +** implementation of PartLogger +*/ + +/** + * constructor for a part logger. + * @param part name of the part (module name) using the logger instance. + */ +PartLogger::PartLogger(const std::string& part) +: Part(part) +{ +} // eo PartLogger::PartLogger(const std.:string&) + + +/** + * @brief constructor for a part logger at module level. + * @param loc the source location where the PartLogger is constructed. + * + * The part name is derived from the filename given with the source location by + * using the basename and cutting off the C++ file suffix (if it is a well known one; + * currently known extensions: cpp, cxx, c++, cc, C). + */ +PartLogger::PartLogger( const SourceLocation& loc ) +{ + if (loc.Line>0 && ! loc.File.empty()) + { + std::string str= basename(loc.File); + Part= remove_suffix(str,".cpp"); + if (Part == str) Part= remove_suffix(str,".cxx"); + if (Part == str) Part= remove_suffix(str,".c++"); + if (Part == str) Part= remove_suffix(str,".cc"); + if (Part == str) Part= remove_suffix(str,".C"); + } + else + { + Part="Unknown"; + } +}// PartLogger::PartLogger(const SourceLocation&) + + +PartLogger::~PartLogger() +{ +} + + +/** + * generic log function. + * @param level the log level. + * @param msg the log message. + */ +void PartLogger::log(int level, const std::string msg) +{ + if (level <= g_max_level) + { + log_part_msg(level, Part, msg); + } +} // eo PartLogger::log(int,const std::string); + + +void PartLogger::fatal(const std::string& msg) +{ + log(LOG_EMERG,msg); +} // eo PartLogger::fatal(const std::string&) + + +void PartLogger::alert(const std::string& msg) +{ + log(LOG_ALERT,msg); +} // eo PartLogger::alert(const std::string&) + + +void PartLogger::critical(const std::string& msg) +{ + log(LOG_CRIT,msg); +} // eo PartLogger::critical(const std::string&) + + +void PartLogger::error(const std::string& msg) +{ + log(LOG_ERR, msg); +} // eo PartLogger::error(const std::string&) + + +void PartLogger::warning(const std::string& msg) +{ + log(LOG_WARNING, msg); +} // eo PartLogger::warning(const std::string&) + + +void PartLogger::notice(const std::string& msg) +{ + log(LOG_NOTICE, msg); +} // eo PartLogger::notice(const std::string&) + + +void PartLogger::info(const std::string& msg) +{ + log(LOG_INFO, msg); +} // eo PartLogger::info(const std::string&) + + +void PartLogger::debug(const std::string& msg) +{ + log(LOG_DEBUG, msg); +} // eo PartLogger::debug(const std::string&) + + + +PartLogger::LogHelper PartLogger::fatal(const SourceLocation& loc) +{ + return PartLogger::LogHelper(*this,LOG_EMERG,loc); +} // eo PartLogger::fatal(const SourceLocation&) + + +PartLogger::LogHelper PartLogger::alert(const SourceLocation& loc) +{ + return PartLogger::LogHelper(*this,LOG_ALERT,loc); +} // eo PartLogger::alert(const SourceLocation&) + + +PartLogger::LogHelper PartLogger::critical(const SourceLocation& loc) +{ + return PartLogger::LogHelper(*this,LOG_CRIT,loc); +} // eo PartLogger::critical(const SourceLocation&) + + +PartLogger::LogHelper PartLogger::error(const SourceLocation& loc) +{ + return PartLogger::LogHelper(*this,LOG_ERR,loc); +} // eo PartLogger::error(const SourceLocation&) + + +PartLogger::LogHelper PartLogger::warning(const SourceLocation& loc) +{ + return PartLogger::LogHelper(*this,LOG_WARNING,loc); +} // eo PartLogger::warning(const SourceLocation&) + + +PartLogger::LogHelper PartLogger::notice(const SourceLocation& loc) +{ + return PartLogger::LogHelper(*this,LOG_NOTICE,loc); +} // eo PartLogger::notice(const SourceLocation&) + + +PartLogger::LogHelper PartLogger::info(const SourceLocation& loc) +{ + return PartLogger::LogHelper(*this,LOG_INFO,loc); +} // eo PartLogger::info(const SourceLocation&) + + +PartLogger::LogHelper PartLogger::debug(const SourceLocation& loc) +{ + return PartLogger::LogHelper(*this,LOG_DEBUG,loc); +} // eo PartLogger::debug(const SourceLocation&) + +/* +** +*/ + + +/** + * enable logging to syslog with a name and a facility. + * @param name the name used as ident. + * @param facility the facility which should be used. + */ +void enable_syslog( const std::string& name, Facility facility ) +{ + close_syslog(); + g_ident= name; + g_facility= facility; + open_syslog(); +} // eo enable_syslog(const std::string,Facility) + + +/** + * enable logging to syslog with a facility. + * The ident is used from a previous call or (if none was set) is + * determined by reading the program path from /proc/\/exe. + * @param facility the facility which should be used. + */ +void enable_syslog( Facility facility ) +{ + if (g_ident.empty()) + { + // determine the program name: + std::string exe_path; + { + std::ostringstream ostr; + ostr << "/proc/" << ::getpid() << "/exe"; + exe_path= ostr.str(); + } + std::string binary_path= read_link(exe_path); + if (!binary_path.empty()) + { + g_ident= basename(binary_path); + } + } + close_syslog(); + g_facility = facility; + open_syslog(); +} // eo enable_syslog(Facility) + + +/** + * enable or disable logging to syslog. + * @param enable whether the logging to syslog should be enabled or not. + */ +void enable_syslog( bool enable ) +{ + if (enable) + { + if (!g_syslog_opened) + { + enable_syslog( g_facility ); + } + } + else // ! enable + { + close_syslog(); + } +} // eo enable_syslog(bool) + + +/** + * enable/ disable loggin to stderr. + * @param enable whether to enable or disable logging to stderr. + */ +void enable_stderr_log(bool enable) +{ + g_stderr_log= enable; +} // eo enableStderr; + + + +/** + * enable logging to a file. + * @param name path to the file. + * + * @note only one log file can be use at a time. + */ +void enable_log_file( const std::string& name ) +{ + g_log_file_name= name; + g_log_stream_ptr.reset( new std::ofstream() ); + g_log_stream_ptr->open( name.c_str(), std::ios::out|std::ios::app ); + //std::cerr << "### opened \"" << name << "\"" << g_log_stream_ptr->good() << std::endl; +} // eo enable_log_file(const std::string&) + + +/** + * enable or disable loggin to a file. + * enabling required a filename to be set by a previous call to + * enable_log_file(const std::string&). + * @param enable whether to enable or disable logging to a file. + */ +void enable_log_file( bool enable ) +{ + if (enable) + { + if (! g_log_file_name.empty()) + { + enable_log_file( g_log_file_name ); + } + } + else // ! enable + { + g_log_stream_ptr.reset(); + } +} // eo enable_log_file(bool) + + +/** + * set a new log level. + * @param level the new log level. + * @return the previous log level. + */ +int set_log_level(int level) +{ + int result = g_max_level; + g_max_level = std::max( LOG_CRIT, level ); + return result; +} // eo set_log_level(int) + + +/** + * returns the current log level. + * @return the current log level. + */ +int get_log_level() +{ + return g_max_level; +} // eo get_log_level() + + +/** + * returns if the current log level covers the given level. + * This is a convenience function for optimization of log output (especially debug output). + * @param level the level which should be tested for. + * @return @a true iff a message with the level would be written out. + */ +bool has_log_level(int level) +{ + return (g_max_level >= level); +} // eo has_log_level(int) + + + +} // eo namespace Logger +} // eo namespace I2n diff --git a/src/logfunc.hpp b/src/logfunc.hpp new file mode 100644 index 0000000..9198eb8 --- /dev/null +++ b/src/logfunc.hpp @@ -0,0 +1,209 @@ +/** @file + * @brief provides logging functions. + * + * @copyright © Copyright 2007-2008 by Intra2net AG + * @license commercial + * + * info@intra2net.com + */ + +#ifndef __I2N_COMMON_LOGFUNC_HPP__ +#define __I2N_COMMON_LOGFUNC_HPP__ + + +#include +#include +#include +#include +#include +#include + +#include + +#include "source_track_basics.hpp" + + + +/* +** +*/ + +namespace I2n +{ +namespace Logger +{ + + + +/** + * wrapper class for syslog facility constants: + * + * @note we don't use an enum here since we import the values from + * the appropriate system header files internally. + */ +struct Facility +{ + + static const int AuthPriv; + static const int Cron; + static const int Daemon; + static const int Kern; + static const int Mail; + static const int News; + static const int Syslog; + static const int User; + static const int UUCP; + static const int Local0; + static const int Local1; + static const int Local2; + static const int Local3; + static const int Local4; + static const int Local5; + static const int Local6; + static const int Local7; + + int m_facility; + + Facility(int facility = Facility::User) : m_facility(facility) {} + + operator int() const { return m_facility; } +}; // eo Facility + + +/** + * struct for log level constants. + */ +struct LogLevel +{ + enum { + Emergency = 0, Fatal= Emergency, + Alert, + Critical, + Error, + Warning, + Notice, + Info, + Debug, + _LogLevel_END + }; + + int m_level; + + LogLevel(int level= Warning) : m_level(level) {} + + operator int() const { return m_level; } +}; // eo struct LogLevel + + +/** + * part logger. + * Provides a logging object to be used within a module (part). + */ +class PartLogger +{ + public: + + class LogHelper + { + public: + virtual ~LogHelper(); + + template< typename T > + std::ostream& operator << (T v) + { + if (StreamPtr.get()) + { + return *StreamPtr << v; + } + throw std::logic_error("pointer vanished"); + } // eo operator <<(T) + + protected: + friend class PartLogger; + + LogHelper(PartLogger& logger, int level, const SourceLocation& loc); + LogHelper(const LogHelper& helper); + + protected: + + PartLogger& Logger; + int Level; + SourceLocation Location; + mutable std::auto_ptr< std::ostringstream > StreamPtr; + }; // eo class LogHelper + + + public: + PartLogger( const std::string& part ); + PartLogger( const SourceLocation& loc ); + virtual ~PartLogger(); + + void log(int level, const std::string msg); + + void fatal(const std::string& msg); + void alert(const std::string& msg); + void critical(const std::string& msg); + void error(const std::string& msg); + void warning(const std::string& msg); + void notice(const std::string& msg); + void info(const std::string& msg); + void debug(const std::string& msg); + + LogHelper fatal(const SourceLocation& loc= SourceLocation()); + LogHelper alert(const SourceLocation& loc= SourceLocation()); + LogHelper critical(const SourceLocation& loc= SourceLocation()); + LogHelper error(const SourceLocation& loc= SourceLocation()); + LogHelper warning(const SourceLocation& loc= SourceLocation()); + LogHelper notice(const SourceLocation& loc= SourceLocation()); + LogHelper info(const SourceLocation& loc= SourceLocation()); + LogHelper debug(const SourceLocation& loc= SourceLocation()); + + protected: + + std::string Part; + +}; // eo class PartLogger + +typedef boost::shared_ptr< PartLogger > PartLoggerPtr; + + +extern PartLogger GlobalLogger; + + +/* +** functions to define the way the logging is done. +** They work globally; i.e. they determine the loggin for the whole program! +*/ + +void enable_syslog( const std::string& name, Facility facility= Facility::User ); +void enable_syslog( Facility facility ); +void enable_syslog( bool enable= true ); + +void enable_stderr_log(bool enable= true ); + +void enable_log_file( const std::string& name ); +void enable_log_file( bool enable= true ); + + +int set_log_level( int level ); +int get_log_level(); +bool has_log_level(int level); + + +/* +** char* versions .... *sigh* +** ( are required, since the compiler doesn't automatically select the const std::string& versions... *big*sigh* ) +*/ + +inline void enable_syslog( const char* name, Facility facility= Facility::User ) +{ + enable_syslog( std::string(name), facility); +} + +inline void enable_log_file( const char* name) { enable_log_file( std::string(name) ); } + + +} // eo namespace Logger +} // eo namespace I2n + +#endif diff --git a/src/source_track_basics.cpp b/src/source_track_basics.cpp new file mode 100644 index 0000000..eb947a6 --- /dev/null +++ b/src/source_track_basics.cpp @@ -0,0 +1,75 @@ +/** @file + * + * @copyright © Copyright 2007-2008 by Intra2net AG + * @license commercial + * + * info@intra2net.com + */ + +#include "source_track_basics.hpp" + +#include +#include +#include +#include + + +namespace I2n +{ + +/* +** implementation of SourceLocation +*/ + +SourceLocation::SourceLocation() +: Line ( 0 ) +{ +} // eo SourceLocation::SourceLocation() + + +SourceLocation::SourceLocation ( + const std::string& file, + long line, + const std::string& funcname +) +: File( file ) +, Line( line ) +, FunctionName( funcname ) +{ +} // eo SourceLocation::SourceLocation(const std::string&,long,const std::string&) + + +/** + * @brief returns a "location tag"; i.e. a string which can be used to identify + * the source location. + * @return the location tag. + */ +std::string SourceLocation::get_location_tag() const +{ + static std::list< std::string > suffixes= TransientPushBackFiller< std::string, std::list >() + ( ".cpp" ) ( ".cxx" ) ( ".cc" ) ( ".C" ) ( ".c++" ) + ( ".hpp" ) ( ".hxx" ) ( ".hh" ) ( ".H" ) ( ".h++" ) ( ".h" ) + ; + if ( LocationTag.empty() and not File.empty() ) + { + std::string str= basename( File ); + for( std::list< std::string >::const_iterator it= suffixes.begin(); + it != suffixes.end(); + ++it ) + { + if ( has_suffix(str,*it) ) + { + str= remove_suffix(str,*it); + break; + } + } + LocationTag= str; + LocationTag+= "#"; + LocationTag+= to_string( Line ); + } + return LocationTag; +} // eo SourceLocation::get_location_tag() + + + +} // eo namespace I2n diff --git a/src/source_track_basics.hpp b/src/source_track_basics.hpp new file mode 100644 index 0000000..9b06f72 --- /dev/null +++ b/src/source_track_basics.hpp @@ -0,0 +1,68 @@ +/** @file + * + * @copyright © Copyright 2007-2008 by Intra2net AG + * @license commercial + * + * info@intra2net.com + */ + +#ifndef __I2N_COMMON_SOURCE_TRACK_BASICS_HPP__ +#define __I2N_COMMON_SOURCE_TRACK_BASICS_HPP__ + +#include +#include + +/* +** some helper macros +*/ + +/** + * macro which encapsulates the construction of a source location. + */ +#define HERE ::I2n::SourceLocation(__FILE__,__LINE__,BOOST_CURRENT_FUNCTION) + + +namespace I2n +{ + + +/** + * (helper) struct for providing a source location. + * Construction should be done with appropriate macros (like @a HERE) + */ +struct SourceLocation +{ + std::string File; + long Line; + std::string FunctionName; + + SourceLocation(); + + SourceLocation( + const std::string& file, + long line, + const std::string& funcname); + + operator bool() const + { + return Line>0 and not File.empty() and not FunctionName.empty(); + } // eo operator bool + + std::string get_function_name() const { return FunctionName; } + std::string get_filename() const { return File; } + long get_line_number() const { return Line; } + + std::string get_location_tag() const; + + private: + + mutable std::string LocationTag; + +}; // eo struct SourceLocation + + + + +} // eo namepsace I2n + +#endif diff --git a/src/tracefunc.cpp b/src/tracefunc.cpp new file mode 100644 index 0000000..c356030 --- /dev/null +++ b/src/tracefunc.cpp @@ -0,0 +1,163 @@ +/** @file + * @brief implementation of tracing functionality. + * + * @copyright © Copyright 2008 by Intra2net AG + * @license commercial + * + * info@intra2net.com + * + * @note This module is not thread safe! + * @todo make this module thread safe (only useful when we can use threads...) + */ + +#include "tracefunc.hpp" + +#include +#include +#include + + +namespace I2n +{ +namespace Tracer +{ + +using Logger::GlobalLogger; + + +namespace +{ + +typedef std::list< ScopeTracker* > ScopeTrackerList; + +ScopeTrackerList scope_tracker_list; + + +std::vector< std::string > indents; + + +/** + * @brief ensures indent level string to exist up to the desired level. + * @param level the desired indent level. + */ +void ensure_indent_level(int level) +{ + while (indents.size() <= level) + { + indents.push_back( indents.back() + " " ); + } +} // eo ensure_indent_level(int) + + +/** + * @brief module initializer. + */ +class __Initializer +{ + public: + __Initializer() + { + // pre initialize indent strings: + indents.clear(); + indents.reserve(10); + indents.push_back(""); + for(int i=10; i-->0;) + { + indents.push_back( indents.back() + " " ); + } + } +} __initializer; + + +} // eo namespace + + +/* +** implementation of ScopeTracker +*/ + +/** + * @brief constructor. initializes object with a source location and emits a ENTER message. + * @param loc the source location. + * + * the message is indented according to the current depth of nested ScopeTracker instances. + */ +ScopeTracker::ScopeTracker( + const SourceLocation& loc) +: Location(loc) +, Depth(0) +, FuncDepth(0) +{ + if (!scope_tracker_list.empty()) + { + ScopeTracker* last_tracker= scope_tracker_list.back(); + if (Location) + { + if (last_tracker->Location + && last_tracker->Location.File == Location.File + && last_tracker->Location.FunctionName == Location.FunctionName) + { + FuncDepth= last_tracker->FuncDepth+1; + } + } + Depth= last_tracker->Depth + 1; + } + ensure_indent_level(Depth); + scope_tracker_list.push_back(this); + { + std::ostringstream ostr; + if (Location.FunctionName.empty()) + { + ostr << " (global scope?)"; + } + else + { + ostr << Location.FunctionName; + if (FuncDepth>0) + { + ostr << "#" << FuncDepth+1; + } + } + Tag= ostr.str(); + } + // spit a message + if (Logger::has_log_level(Logger::LogLevel::Debug)) + { + GlobalLogger.debug() << indents[Depth] << "ENTER " << Tag; + } +} // eo ScopeTrcaker::ScopeTracker(const SourceLocation&) + + +/** + * @brief destructor. emits a LEAVE message. + * + * + */ +ScopeTracker::~ScopeTracker() +{ + // spit a message + if (Logger::has_log_level(Logger::LogLevel::Debug)) + { + GlobalLogger.debug() << indents[Depth] << "LEAVE " << Tag; + } + if (scope_tracker_list.empty()) + { + return; + } + if (scope_tracker_list.back() == this) + { + scope_tracker_list.pop_back(); + } + else + { + // oh hell, this should never be the case! + //TODO + GlobalLogger.error() << "inconsistency detected in scope tracker"; + } +} // eo ScopeTracker::~ScopeTracker() + + + + +} // eo namespace Tracer +} // eo namespace I2n diff --git a/src/tracefunc.hpp b/src/tracefunc.hpp new file mode 100644 index 0000000..a110313 --- /dev/null +++ b/src/tracefunc.hpp @@ -0,0 +1,63 @@ +/** @file + * @brief provides tracing funtionality. + * + * Provides a scope tracker + * + * @copyright © Copyright 2008 by Intra2net AG + * @license commercial + * + * info@intra2net.com + */ + +#ifndef __I2N_COMMON_TRACEFUNC_HPP__ +#define __I2N_COMMON_TRACEFUNC_HPP__ + +#include +#include "logfunc.hpp" +#include "source_track_basics.hpp" + + +namespace I2n +{ +namespace Tracer +{ + + +/** + * @brief scope tracker class. + * + * basically: emits a ENTER message on construction and a LEAVE message on destruction. + * And indent these messages acoording to the nesting depth. + */ +class ScopeTracker +{ + public: + ScopeTracker(const SourceLocation& loc); + ~ScopeTracker(); + + private: + ScopeTracker(const ScopeTracker& rhs); + ScopeTracker& operator = (const ScopeTracker& rhs); + + SourceLocation Location; + int Depth; + int FuncDepth; + + std::string Tag; +}; // eo ScopeTracker + + +/* +** helper macros: +*/ + +/** + * @brief constructs a scope tracker. + */ +#define SCOPETRACKER() ::I2n::Tracer::ScopeTracker __scope_tracker(HERE) + + +} // eo namespace Tracer +} // eo namespace I2n + +#endif diff --git a/test/Makefile.am b/test/Makefile.am index f3ef13a..25b8af3 100644 --- a/test/Makefile.am +++ b/test/Makefile.am @@ -2,7 +2,7 @@ INCLUDES = -I$(top_srcdir)/src @CPPUNIT_CFLAGS@ METASOURCES = AUTO check_PROGRAMS = test test_SOURCES = ip_range.cpp stringfunc.cpp test.cpp test_containerfunc.cpp \ - test_filefunc.cpp test_pidfile.cpp + test_filefunc.cpp test_logging.cpp test_pidfile.cpp test_LDADD = $(top_builddir)/src/libi2ncommon.la @CPPUNIT_LIBS@ TESTS = test diff --git a/test/test_logging.cpp b/test/test_logging.cpp new file mode 100644 index 0000000..8758e1f --- /dev/null +++ b/test/test_logging.cpp @@ -0,0 +1,132 @@ +/** @file + * test cases for loggin module. + * + * (c) Copyright 2007 by Intra2net AG + * + * info@intra2net.com + */ + +//#define NOISEDEBUG + +#include +#include +#include +#include + + +#include +#include +#include + +#include +#include + + +#ifdef NOISEDEBUG +#define DOUT(msg) std::cout << msg << std::endl +#else +#define DOUT(msg) do {} while (0) +#endif + + +using namespace CppUnit; + +using namespace I2n; + +namespace { + +Logger::PartLogger module_logger(HERE); + +} // eo namespace + + + +class TestLogging : public TestFixture +{ + CPPUNIT_TEST_SUITE(TestLogging); + + CPPUNIT_TEST(Syslog1); + CPPUNIT_TEST(TestScopeTrace1); + + CPPUNIT_TEST_SUITE_END(); + + public: + + void setUp() + { + } // eo setUp + + + void tearDown() + { + Logger::set_log_level(0); + Logger::enable_syslog(false); + Logger::enable_stderr_log(false); + Logger::enable_log_file(false); + } // eo tearDown + + + /* + * the tests: + */ + + + void Syslog1() + { + //Logger::enable_syslog("I2N Unittest", Logger::Facility::User ); + Logger::enable_syslog( Logger::Facility::User ); + Logger::PartLogger log(__func__); + + Logger::set_log_level( 7 ); + Logger::enable_stderr_log(); + Logger::enable_log_file("zzUnitTest.log"); + + log.error("Test error msg"); + log.error() << "Stream test error msg #" << 2 << "."; + log.warning() << "Stream test warning msg"; + log.notice() << "Stream test notice msg"; + log.info() << "Stream test info msg"; + log.debug() << "Stream test debug msg"; + + log.debug() + << "multiline log message" << std::endl + << "this (second) line should be indented" << std::endl + << "and this also!" << std::endl; + log.debug(HERE) << "This should have a source info"; + + module_logger.debug(HERE) << "module level debug message with source loc info"; + } // eo Syslog1() + + + + void TestScopeTrace1() + { + //Logger::enable_syslog("I2N Unittest", Logger::Facility::User ); + Logger::enable_syslog( Logger::Facility::User ); + Logger::PartLogger log(__func__); + + Logger::set_log_level( 7 ); + Logger::enable_stderr_log(); + Logger::enable_log_file("zzUnitTest.log"); + + SCOPETRACKER(); + + log.notice() << "Stream test notice msg"; + { + SCOPETRACKER(); // #2 + { + SCOPETRACKER(); // #3 + { + SCOPETRACKER(); // #4 + } + } + { + SCOPETRACKER(); // #3 + } + } + } // eo TestScopeTrace1() + +}; // eo class TestLogging + + +CPPUNIT_TEST_SUITE_REGISTRATION(TestLogging); -- 1.7.1