log.h

Go to the documentation of this file.
00001 /**
00002  *  Copyright (C) 2004-2005 Alo Sarv <madcat_@users.sourceforge.net>
00003  *
00004  *  This program is free software; you can redistribute it and/or modify
00005  *  it under the terms of the GNU General Public License as published by
00006  *  the Free Software Foundation; either version 2 of the License, or
00007  *  (at your option) any later version.
00008  *
00009  *  This program is distributed in the hope that it will be useful,
00010  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
00011  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00012  *  GNU General Public License for more details.
00013  *
00014  *  You should have received a copy of the GNU General Public License
00015  *  along with this program; if not, write to the Free Software
00016  *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
00017  */
00018 
00019 #ifndef __LOG_H__
00020 #define __LOG_H__
00021 
00022 /**
00023  * \file log.h Interface for Logging Subsystem
00024  */
00025 
00026 /**
00027  * @page logsubsys Logging Subsystem Overview
00028  *
00029  * Logging Subsystem is built up from a set of globally available functions
00030  * as follows:
00031  *
00032  * logMsg()     - Logs a message
00033  * logDebug()   - Logs a message only in Debug build (when NDEBUG isn't defined)
00034  * logWarning() - Logs a warning message (prepends with "Warning:")
00035  * logError()   - Logs an error message (preprends with "Error:")
00036  * logFatalError() - Logs a fatal system error and abort()'s the app
00037  * logTrace()   - Logs a message only if the given trace mask is enabled
00038  *
00039  * The logging functions use Log class as backend for various data storage,
00040  * for example tracemasks, and (in the future) other logging targets besides
00041  * the standard error device.
00042  *
00043  * Each logging function is duplicated - one version for std::string argument
00044  * (for usage with literal strings), and second version for boost::format
00045  * argument (for usage of printf()-style formatting). Thus the following
00046  * code is a sample usage of this system:
00047  *
00048  *  Note - I had to escape few %'s for the Doxygen HTML-generation work
00049  *  correctly on this file - you'll get the point.
00050  *
00051  * <pre>
00052  *     static const int LOGTRACE = 1;
00053  *     logMsg(boost::format("Hello %s, This is logtest#%d") \% "Madcat" \% 10);
00054  *     logDebug("This is a debug message.");
00055  *     logDebug(boost::format("This is %dnd debug message.") \% 2);
00056  *     logError("And this is a serious error.");
00057  *     logError(boost::format("Actually, this one is too. Errorcode: %d") \% 5);
00058  *
00059  *     logTrace(LOGTRACE, "This will not be seen.");
00060  *     Log::addTraceMask(LOGTRACE, "LogTrace");
00061  *     logTrace(LOGTRACE, "This is a Log Trace.");
00062  *     Log::remTraceMask(LOGTRACE);
00063  *     logTrace(LOGTRACE, "This will not be seen.");
00064  * </pre>
00065  * The output produced by the above code should be:
00066  * <pre>
00067  *     Hello Madcat, This is logtest#10
00068  *     Debug: This is a debug message.
00069  *     Debug: This is 2nd debug message.
00070  *     Error: And this is a serious error.
00071  *     Error: Actually, this one is too. Errorcode: 5
00072  *     Trace(LogTrace): This is a Log Trace.
00073  * </pre>
00074  */
00075 
00076 #include <hn/osdep.h>
00077 #include <boost/format.hpp>
00078 #include <boost/thread.hpp>
00079 #include <boost/function.hpp>
00080 #include <boost/signals.hpp>
00081 #include <boost/bind.hpp>
00082 #include <map>
00083 #include <set>
00084 #include <vector>
00085 #include <deque>
00086 #include <string>
00087 #include <iostream>
00088 #include <fstream>
00089 
00090 /**
00091  * These trace masks are used internally by the framework's various classes.
00092  * Users of the framework are required to use string trace masks instead of
00093  * integer trace masks to avoid masks overlapping between modules.
00094  */
00095 enum InternalTraceMasks {
00096         TRACE_HASH = 0x01,    //!< Used in Hash classes
00097         TRACE_MD,             //!< Used in MetaData classes
00098         TRACE_MOD,            //!< Used by ModManager class
00099         TRACE_SOCKET,         //!< Used by Sockets classes
00100         TRACE_PARTDATA,       //!< Used by PartData class
00101         TRACE_SHAREDFILE,     //!< Used by SharedFile class
00102         TRACE_FILESLIST,      //!< Used by FilesList class
00103         TRACE_OBJECT,         //!< Used by Object class
00104         TRACE_HT,             //!< Used by HashThread class
00105         TRACE_SCHED,          //!< Used by Scheduler class
00106         TRACE_RANGE,          //!< Used by Range Management Subsystem
00107         TRACE_EVENT,          //!< Used by Event Handling Subsystem
00108         TRACE_CONFIG          //!< Used by Config class
00109 };
00110 
00111 //! Different message types
00112 enum MessageType {
00113         MT_MSG     = 0x01,  //!< Normal, informative messages
00114         MT_DEBUG   = 0x02,  //!< Debugging messages, disabled in release mode
00115         MT_TRACE   = 0x04,  //!< Trace messages, disabled in release mode
00116         MT_ERROR   = 0x08,  //!< Error messages
00117         MT_WARNING = 0x10   //!< Warnings
00118 };
00119 
00120 /**
00121  * Logging wrapper providing shell for trace masks and strings.
00122  * Only static members are accessible and calls are forwarded to the
00123  * only instance of this class, accessible via instance() member.
00124  */
00125 class DLLEXPORT Log {
00126 public:
00127         //! Retrieve the only instance of this class
00128         static Log& instance();
00129 
00130         /**
00131          * Enable a trace mask.
00132          *
00133          * @param traceInt   Integer value. logTrace() calls with this
00134          *                   value will now be enabled.
00135          * @param traceStr   String for the trace messages - will be included
00136          *                   in the log messages.
00137          */
00138         void enableTraceMask(int traceInt, const std::string &traceStr) {
00139                 m_traceMasks[traceInt] = traceStr;
00140         }
00141 
00142         /**
00143          * Enable a string trace mask. logTrace() calls using this mask will
00144          * now be displayed.
00145          *
00146          * @param mask      String mask to enable.
00147          */
00148         void enableTraceMask(const std::string &mask) {
00149                 m_enabledStrMasks.insert(mask);
00150         }
00151 
00152         /**
00153          * Add a string trace mask to supported list of string masks. The
00154          * mask can later be enabled using enableTraceMask() function.
00155          *
00156          * @param mask       Mask to be added.
00157          *
00158          * \note This function is used to 'publish' the availability of a trace
00159          * mask for others (e.g. user interfaces et al).
00160          */
00161         void addTraceMask(const std::string &mask) {
00162                 m_strMasks.insert(mask);
00163         }
00164 
00165         /**
00166          * Disable a trace mask. Messages with this mask will no longer be
00167          * shown.
00168          *
00169          * @param traceInt   Integer value of the traceMask to be removed.
00170          */
00171         void disableTraceMask(int traceInt) {
00172                 m_traceMasks.erase(traceInt);
00173         }
00174 
00175         /**
00176          * Disable a string trace mask.
00177          *
00178          * @param mask       Mask to be disabled.
00179          */
00180         void disableTraceMask(const std::string &mask) {
00181                 m_enabledStrMasks.erase(mask);
00182         }
00183 
00184         /**
00185          * Erase a string trace mask from available string masks.
00186          *
00187          * @param mask       Mask to be removed.
00188          *
00189          * \note This also disables the mask.
00190          */
00191         void removeTraceMask(const std::string &mask) {
00192                 disableTraceMask(mask);
00193                 m_strMasks.erase(mask);
00194         }
00195 
00196         /**
00197          * Check if a trace mask is enabled
00198          *
00199          * @param traceMask      Mask to be checked
00200          * @return               True if the mask is currently enabled
00201          */
00202         bool hasTraceMask(int traceMask) {
00203                 MIter i = m_traceMasks.find(traceMask);
00204                 if (i != m_traceMasks.end()) {
00205                         return true;
00206                 }
00207                 return false;
00208         }
00209 
00210         /**
00211          * Retrieve string corresponding to a trace mask
00212          *
00213          * @param traceMask      Mask for which to look up the string
00214          * @return               String corresponding to the trace mask
00215          */
00216         static std::string getTraceStr(int traceMask) {
00217                 MIter i = instance().m_traceMasks.find(traceMask);
00218                 if (i != instance().m_traceMasks.end()) {
00219                         return (*i).second;
00220                 }
00221                 return std::string("");
00222         }
00223 
00224         /**
00225          * Add a new log file target where all log messages will be
00226          * written to.
00227          *
00228          * @param filename     Path to log file.
00229          */
00230         void addLogFile(const std::string &filename) {
00231                 boost::mutex::scoped_lock l(m_filesLock);
00232                 m_logFiles.push_back(filename);
00233         }
00234 
00235         /**
00236          * Write specified string to all output streams.
00237          *
00238          * @param msg        String to write.
00239          *
00240          * \note msg is passed by value instead of reference since this function
00241          *       clears up any escaped symbols (e.g. colors) from the message.
00242          */
00243         void sendToFiles(std::string msg);
00244 
00245         /**
00246          * Actual message logging.
00247          */
00248         void doLogString(MessageType t, const std::string &msg);
00249 
00250         /**
00251          * Log a trace string only if the trace mask has been enabled.
00252          */
00253         void doLogString(int traceMask, const std::string &msg) {
00254                 boost::recursive_mutex::scoped_lock l(s_iosLock);
00255                 if (Log::hasTraceMask(traceMask)) {
00256                         doLogString(
00257                                 MT_TRACE,
00258                                 (boost::format("Trace(%s): %s") %
00259                                 Log::getTraceStr(traceMask) % msg).str()
00260                         );
00261                 } else {
00262                         addMsg(MT_TRACE, msg);
00263                 }
00264         }
00265 
00266         void doLogString(const std::string &mask, const std::string &msg) {
00267                 boost::recursive_mutex::scoped_lock l(s_iosLock);
00268                 if (m_enabledStrMasks.find(mask) != m_enabledStrMasks.end()) {
00269                         doLogString(
00270                                 MT_TRACE,
00271                                 (boost::format("Trace(%s): %s") %
00272                                 mask % msg).str()
00273                         );
00274                 } else {
00275                         addMsg(MT_TRACE, msg);
00276                 }
00277         }
00278 
00279         /**
00280          * Print last messages to std::cerr.
00281          *
00282          * @param count       Number of last messages to print.
00283          */
00284         static void printLast(uint32_t count);
00285 
00286         /**
00287          * Retrieve the last logged message.
00288          *
00289          * @return         Last logged message.
00290          */
00291         static std::string getLastMsg() {
00292                 return s_messages.back();
00293         }
00294 
00295         /**
00296          * Retrieve last N messages.
00297          *
00298          * @param count      Number of messages to retrieve.
00299          * @param cont       Container to push the messages.
00300          */
00301         static void getLast(uint32_t count, std::vector<std::string> *cont);
00302 
00303         /**
00304          * Add a string to prepend to all log messages.
00305          *
00306          * @str               String to prepend to log messages.
00307          */
00308         void addPreStr(const std::string &str) {
00309                 m_preStr += str;
00310         }
00311 
00312         /**
00313          * Remove a string prepended to all log messages.
00314          *
00315          * @param str         String to be removed.
00316          */
00317         void remPreStr(const std::string &str) {
00318                 size_t i = m_preStr.rfind(str);
00319                 if (i != std::string::npos) {
00320                         m_preStr.erase(i, str.size());
00321                 }
00322         }
00323 
00324         /**
00325          * Add a handler function which will be passed all log messages which
00326          * match the flags specified.
00327          *
00328          * @param h          Function object taking const std::string& argument
00329          * @param flags      Types of log messages to be passed to the handler.
00330          *                   This is a bit-field containing one or more
00331          *                   MessageType enumeration values.
00332          * Example:
00333          * addHandler(LOG_HANDLER(&MyClass::onLogMsg), MT_ERROR|MT_WARNING);
00334          */
00335         boost::signals::connection addHandler(
00336                 boost::function<void (const std::string&, MessageType type)> ha
00337         ) {
00338                 return m_sig.connect(ha);
00339         }
00340 
00341         /**
00342          * More convenient version of the above function, performs function
00343          * object binding internally.
00344          */
00345         template<typename T>
00346         boost::signals::connection addHandler(
00347                 T *obj, void (T::*ha)(const std::string&, MessageType)
00348         ) {
00349                 return addHandler(boost::bind(ha, obj, _1, _2));
00350         }
00351 private:
00352         Log();                         //!< Constructor
00353         Log(Log&);                     //!< Forbidden
00354         Log& operator=(const Log&);    //!< Forbidden
00355         ~Log();                        //! Destructor
00356 
00357         //! Pointer to the single instance of this class
00358         static Log *s_log;
00359 
00360         /**
00361          * Adds messages to s_messages array.
00362          */
00363         void addMsg(MessageType t, const std::string &msg) {
00364                 Log::s_messages.push_back(msg);
00365                 if (Log::s_messages.size() > 100) {
00366                         Log::s_messages.pop_front();
00367                 }
00368                 m_sig(msg, t);
00369         }
00370 
00371         //! Enabled trace masks
00372         std::map<int, std::string> m_traceMasks;
00373         std::set<std::string> m_strMasks;
00374         std::set<std::string> m_enabledStrMasks;
00375 
00376         //! Output streams to write to
00377         std::vector<std::string> m_logFiles;
00378 
00379         //! Iterator for the above list
00380         typedef std::vector<std::string>::iterator FIter;
00381 
00382         //! Protects m_logFiles list and the streams contained within
00383         boost::mutex m_filesLock;
00384 
00385         //! Stores last N log messages (including trace and debug messages)
00386         static std::deque<std::string> s_messages;
00387 
00388         //! Iterator typedef for the above list
00389         typedef std::deque<std::string>::const_iterator CMIter;
00390 
00391         //! For easier usaage
00392         typedef std::map<int, std::string>::iterator MIter;
00393 
00394         //! String to prepend to all log messages.
00395         std::string m_preStr;
00396 
00397         //! Protects output streams for multiple thread access.
00398         static boost::recursive_mutex s_iosLock;
00399 
00400         /** @name Various logging functions. */
00401         //@{
00402         friend void DLLEXPORT logMsg       (const std::string &msg  );
00403         friend void DLLEXPORT logMsg       (const boost::format &fmt);
00404         friend void DLLEXPORT logDebug     (const std::string &msg  );
00405         friend void DLLEXPORT logDebug     (const boost::format &fmt);
00406         friend void DLLEXPORT logWarning   (const std::string &msg  );
00407         friend void DLLEXPORT logWarning   (const boost::format &fmt);
00408         friend void DLLEXPORT logError     (const std::string &msg  );
00409         friend void DLLEXPORT logError     (const boost::format &fmt);
00410         friend void DLLEXPORT logFatalError(const std::string &msg  );
00411         friend void DLLEXPORT logFatalError(const boost::format &msg);
00412 #if !defined(__FULL_TRACE__) && !defined(NDEBUG) && !defined(NTRACE)
00413         friend void DLLEXPORT logTrace(
00414                 uint32_t mask, const std::string &msg
00415         );
00416         friend void DLLEXPORT logTrace(
00417                 uint32_t mask, const boost::format &msg
00418         );
00419         friend void DLLEXPORT logTrace(
00420                 const std::string &mask, const std::string &msg
00421         );
00422         friend void DLLEXPORT logTrace(
00423                 const std::string &mask, const boost::format &msg
00424         );
00425 #endif
00426         //@}
00427 
00428         boost::signal<void (const std::string&, MessageType)> m_sig;
00429 };
00430 
00431 /**
00432  * Logs a message only if given trace mask is enabled.
00433  *
00434  * @param mask     TraceMask. Message will only be logged if this mask has been
00435  *                 previously enabled from Log class with addTraceMask function.
00436  * @param msg      Message to be logged.
00437  *
00438  * If __FULL_TRACE__ is defined, we use macro in order to retrieve the source
00439  * file/line of the call. Otherwise, inline functions are used.
00440  */
00441 #if defined(__FULL_TRACE__)
00442 #define logTrace(mask, msg) \
00443         Log::instance().doLogString( \
00444                 MT_TRACE,  \
00445                 mask,  \
00446                 (boost::format("%s:%d: %s") % (__FILE__) % (__LINE__) \
00447                 % (msg)).str() \
00448         )
00449 #elif defined(NDEBUG) || defined(NTRACE)
00450         #define logTrace(mask, text)
00451 #endif
00452 
00453 #endif