/* * 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 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. */ 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) { m_processName = name; } static std::string getProcessName() { return m_processName; } #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, ...); virtual ~Logger() {} /** * 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; /** redirect into messagev() */ 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 ; protected: static std::string m_processName; }; /** * Global logging, implemented as a singleton with one instance per * process. * * @TODO avoid global variable */ class LoggerBase : public Logger { public: LoggerBase() : m_level(INFO) {} /** * 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 LoggerBase &instance(); /** * Overrides the default Logger implementation. The Logger class * itself will never delete the active logger. * * @param logger will be used for all future logging activities */ static void pushLogger(LoggerBase *logger); /** * Remove the current logger and restore previous one. * Must match a pushLogger() call. */ static void popLogger(); /** total number of active loggers */ static int numLoggers(); /** * access to active logger * @param index 0 for oldest (inner-most) logger * @return pointer or NULL for invalid index */ static LoggerBase *loggerAt(int index); 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; }; /** * 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::LoggerBase::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