/* * Copyright (C) 2009 Patrick Ohly * Copyright (C) 2009 Intel Corporation * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) version 3. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301 USA */ #ifndef INCL_LOGGING #define INCL_LOGGING #include #include #include #ifdef HAVE_CONFIG_H # include #endif #ifdef HAVE_GLIB # include #endif #include #include #include #include #include #include SE_BEGIN_CXX /** * Abstract interface for logging in SyncEvolution. Can be * implemented by other classes to add information (like a certain * prefix) before passing the message on to a global instance for the * actual processing. * * The static methods provide some common utility code and manage a * global stack of loggers. The one pushed latest is called first to * handle a new message. It can find its parent logger (= the one * added just before it) and optionally pass the message up the chain * before or after processing it itself. * * All methods must be thread-safe. */ class Logger { public: /** * Which of these levels is the right one for a certain message * is a somewhat subjective choice. Here is a definition how they * are supposed to be used: * - error: severe problem which the user and developer have to * know about * - warning: a problem that was handled, but users and developers * probably will want to know about * - info: information about a sync session which the user * will want to read during/after each sync session * - developer: information about a sync session that is not * interesting for a user (for example, because it * is constant and already known) but which should * be in each log because developers need to know * it. Messages logged with this calls will be included * at LOG_LEVEL_INFO, therefore messages should be small and * not recur so that the log file size remains small. * - debug: most detailed logging, messages may be arbitrarily large * * Here is a decision tree which helps to pick the right level: * - an error: => ERROR * - a non-fatal error: => WARNING * - it changes during each sync or marks important steps * in the sync: INFO * - same as before, but without the [INFO] prefix added to each line: => SHOW * - small, non-recurring message which is important for developers * who read a log produced at LOG_LEVEL_INFO: DEVELOPER * - everything else: DEBUG */ typedef enum { /** * no error messages printed */ NONE = -1, /** * only error messages printed */ ERROR, /** * error and warning messages printed */ WARNING, /** * "Normal" stdout output which is meant to be seen by a * user. */ SHOW, /** * errors and info messages for users and developers will be * printed: use this to keep the output consise and small */ INFO, /** * important messages to developers */ DEV, /** * all messages will be printed, including detailed debug * messages */ DEBUG } Level; static const char *levelToStr(Level level); /** always returns a valid level, also for NULL, by falling back to DEBUG */ static Level strToLevel(const char *str); /** * additional, short string identifying the SyncEvolution process; * empty if master process * * Included by LoggerStdout in the [INFO/DEBUG/...] tag. */ static void setProcessName(const std::string &name); static std::string getProcessName(); /** * Obtains the recursive logging mutex. * * All calls offered by the Logger class already lock that mutex * internally, but sometimes it may be necessary to protect a larger * region of logging related activity. */ static RecMutex::Guard lock(); #ifdef HAVE_GLIB /** * can be used in g_log_set_handler() to redirect log messages * into our own logging; must be called for each log domain * that may be relevant */ static void glogFunc(const gchar *logDomain, GLogLevelFlags logLevel, const gchar *message, gpointer userData); #endif /** * can be used as replacement for libsynthesis console printf function, * logs at DEBUG level * * @param stream is ignored * @param format guaranteed to start with "SYSYNC " * @return always 0 */ static int sysyncPrintf(FILE *stream, const char *format, ...); Logger(); virtual ~Logger(); /** * Prepare logger for removal from logging stack. May be called * multiple times. * * The logger should stop doing anything right away and just pass * on messages until it gets deleted eventually. */ virtual void remove() throw () {} /** * Collects all the parameters which may get passed to * messagev. */ class MessageOptions { public: /** level for current message */ Level m_level; /** inserted at beginning of each line, if non-NULL */ const std::string *m_prefix; /** source file where message comes from, if non-NULL */ const char *m_file; /** source line number, if file is non-NULL */ int m_line; /** surrounding function name, if non-NULL */ const char *m_function; /** name of the process which originally created the message, if different from current one */ const std::string *m_processName; MessageOptions(Level level); MessageOptions(Level level, const std::string *prefix, const char *file, int line, const char *function); }; /** * output a single message * * @param options carries additional information about the message * @param format sprintf format * @param args parameters for sprintf: consumed by this function, * make copy with va_copy() if necessary! */ virtual void messagev(const MessageOptions &options, const char *format, va_list args) = 0; /** * A convenience and backwards-compatibility class which allows * calling some methods of the underlying pointer directly similar * to the Logger reference returned in previous SyncEvolution * releases. */ class Handle { boost::shared_ptr m_logger; public: Handle() throw (); Handle(Logger *logger) throw (); template Handle(const boost::shared_ptr &logger) throw () : m_logger(logger) {} template Handle(const boost::weak_ptr &logger) throw () : m_logger(logger.lock()) {} Handle(const Handle &other) throw (); Handle &operator = (const Handle &other) throw (); ~Handle() throw (); operator bool () const { return m_logger; } bool operator == (Logger *logger) const { return m_logger.get() == logger; } Logger *get() const { return m_logger.get(); } void messagev(const MessageOptions &options, const char *format, va_list args) { m_logger->messagev(options, format, args); } void message(Level level, const std::string *prefix, const char *file, int line, const char *function, const char *format, ...) #ifdef __GNUC__ __attribute__((format(printf, 7, 8))) #endif ; void message(Level level, const std::string &prefix, const char *file, int line, const char *function, const char *format, ...) #ifdef __GNUC__ __attribute__((format(printf, 7, 8))) #endif ; void messageWithOptions(const MessageOptions &options, const char *format, ...) #ifdef __GNUC__ __attribute__((format(printf, 3, 4))) #endif ; void setLevel(Level level) { m_logger->setLevel(level); } Level getLevel() { return m_logger->getLevel(); } void remove() throw () { m_logger->remove(); } }; /** * Grants access to the singleton which implements logging. * The implementation of this function and thus the Log * class itself is platform specific: if no Log instance * has been set yet, then this call has to create one. */ static Handle instance(); /** * Overrides the current default Logger implementation. * * @param logger will be used for all future logging activities */ static void addLogger(const Handle &logger); /** * Remove the specified logger. * * Note that the logger might still be in use afterwards, for * example when a different thread currently uses it. Therefore * loggers should be small stub classes. If they need access to * more expensive classes to do their work, they shold hold weak * reference to those and only lock them when logging. */ static void removeLogger(Logger *logger); virtual void setLevel(Level level) { m_level = level; } virtual Level getLevel() { return m_level; } protected: /** * Prepares the output. The result is passed back to the caller * line-by-line (expectedTotal > 0) and/or as full chunk * (expectedTotal = 0). The expected size is just a guess, be * prepared to handle more output. * * Each chunk already includes the necessary line breaks (in * particular after the last line when it contains the entire * output). It may be modified by the callback. * * @param processName NULL means use the current process' name, * empty means use none */ void formatLines(Level msglevel, Level outputlevel, const std::string *processName, const std::string *prefix, const char *format, va_list args, boost::function print); private: Level m_level; /** * Set by formatLines() before writing the first message if log * level is debugging, together with printing a message that gives * the local time. */ Timespec m_startTime; }; /** * Takes a logger and adds it to the stack * as long as the instance exists. */ template class PushLogger : boost::noncopyable { Logger::Handle m_logger; public: PushLogger() {} /** * Can use Handle directly here. */ PushLogger(const Logger::Handle &logger) : m_logger(logger) { if (m_logger) { Logger::addLogger(m_logger); } } /** * Take any type that a Handle constructor accepts, then use it as * Handle. */ template PushLogger(M logger) : m_logger(Logger::Handle(logger)) { if (m_logger) { Logger::addLogger(m_logger); } } ~PushLogger() throw () { if (m_logger) { Logger::removeLogger(m_logger.get()); } } operator bool () const { return m_logger; } void reset(const Logger::Handle &logger) { if (m_logger) { Logger::removeLogger(m_logger.get()); } m_logger = logger; if (m_logger) { Logger::addLogger(m_logger); } } template void reset(M logger) { if (m_logger) { Logger::removeLogger(m_logger.get()); } m_logger = Logger::Handle(logger); if (m_logger) { Logger::addLogger(m_logger); } } void reset() { if (m_logger) { Logger::removeLogger(m_logger.get()); } m_logger = Logger::Handle(); } L *get() { return static_cast(m_logger.get()); } L * operator -> () { return get(); } }; /** * Wraps Logger::message() in the current default logger. * and adds file and line where the message comes from. * * This macro reverses _prefix and _level to avoid the situation where * the compiler mistakes a NULL _prefix with the _format parameter * (happened once while doing code refactoring). * * @TODO make source and line info optional for release * @TODO add function name (GCC extension) */ #define SE_LOG(_prefix, _level, _format, _args...) \ SyncEvo::Logger::instance().message(_level, \ _prefix, \ __FILE__, \ __LINE__, \ NULL, \ _format, \ ##_args); #define SE_LOG_SHOW(_prefix, _format, _args...) SE_LOG(_prefix, SyncEvo::Logger::SHOW, _format, ##_args) #define SE_LOG_ERROR(_prefix, _format, _args...) SE_LOG(_prefix, SyncEvo::Logger::ERROR, _format, ##_args) #define SE_LOG_WARNING(_prefix, _format, _args...) SE_LOG(_prefix, SyncEvo::Logger::WARNING, _format, ##_args) #define SE_LOG_INFO(_prefix, _format, _args...) SE_LOG(_prefix, SyncEvo::Logger::INFO, _format, ##_args) #define SE_LOG_DEV(_prefix, _format, _args...) SE_LOG(_prefix, SyncEvo::Logger::DEV, _format, ##_args) #define SE_LOG_DEBUG(_prefix, _format, _args...) SE_LOG(_prefix, SyncEvo::Logger::DEBUG, _format, ##_args) SE_END_CXX #endif // INCL_LOGGING