// -*- indent-tabs-mode: nil -*- #ifndef __ARC_LOGGER__ #define __ARC_LOGGER__ #include #include #include #include #include #include #include namespace Arc { /** \addtogroup common * @{ */ /// Logging levels for tagging and filtering log messages. enum LogLevel { DEBUG = 1, ///< DEBUG level designates finer-grained informational events ///< which should only be used for debugging purposes. VERBOSE = 2, ///< VERBOSE level designates fine-grained informational events ///< that will give additional information about the application. INFO = 4, ///< INFO level designates informational messages that highlight ///< the progress of the application at coarse-grained level. WARNING = 8, ///< WARNING level designates potentially harmful situations. ERROR = 16, ///< ERROR level designates error events that might still allow ///< the application to continue running. FATAL = 32 ///< FATAL level designates very severe error events that will ///< presumably lead the application to abort. }; /// Output formats. Defines prefix for each message. enum LogFormat { /// All information about message is printed LongFormat, /// All information except domain is printed /** * \since Added in 4.0.0. **/ MediumFormat, /// Only message level is printed ShortFormat, /// Message time (microsecond precision) and time difference /// from previous message are printed. This format is mostly /// meant for profiling. DebugFormat, /// Only message is printed EmptyFormat }; /// Struct to contain LogFormat, to use with operator<<(std::ostream&, const LoggerFormat&) struct LoggerFormat { /// Make a new LoggerFormat with the given LogFormat. LoggerFormat(LogFormat format) : format(format) {}; LogFormat format; }; /// Allows printing of messages to streams using ARC Logger formatting. std::ostream& operator<<(std::ostream& os, const LoggerFormat& format); /// Printing of LogLevel values to ostreams. /** Output operator so that LogLevel values can be printed in a nicer way. */ std::ostream& operator<<(std::ostream& os, LogLevel level); /// Convert string to a LogLevel. LogLevel string_to_level(const std::string& str); /// Convert string case-insensitively to a LogLevel /** * \since Added in 4.1.0. **/ LogLevel istring_to_level(const std::string& llStr); /// Case-insensitive parsing of a string to a LogLevel with error response. /** * The method will try to parse (case-insensitive) the argument string * to a corresponding LogLevel. If the method succeeds, true will * be returned and the argument \a ll will be set to * the parsed LogLevel. If the parsing fails \c false will be * returned. The parsing succeeds if \a llStr match * (case-insensitively) one of the names of the LogLevel members. * * @param llStr a string which should be parsed to a Arc::LogLevel. * @param ll a Arc::LogLevel reference which will be set to the * matching Arc::LogLevel upon successful parsing. * @return \c true in case of successful parsing, otherwise \c false. * @see LogLevel */ bool istring_to_level(const std::string& llStr, LogLevel& ll); /// Same as istring_to_level except it is case-sensitive. bool string_to_level(const std::string& str, LogLevel& ll); /// Convert LogLevel to a string. std::string level_to_string(const LogLevel& level); /// Convert an old-style log level (int from 0 to 5) to a LogLevel. LogLevel old_level_to_level(unsigned int old_level); /// A class for log messages. /** This class is used to represent log messages internally. It contains the time the message was created, its level, from which domain it was sent, an identifier and the message text itself. \headerfile Logger.h arc/Logger.h */ class LogMessage { public: /// Creates a LogMessage with the specified level and message text. /** This constructor creates a LogMessage with the specified level and message text. The time is set automatically, the domain is set by the Logger to which the LogMessage is sent and the identifier is composed from the process ID and the address of the Thread object corresponding to the calling thread. @param level The level of the LogMessage. @param message The message text. */ LogMessage(LogLevel level, const IString& message); /// Creates a LogMessage with the specified attributes. /** This constructor creates a LogMessage with the specified level, message text and identifier. The time is set automatically and the domain is set by the Logger to which the LogMessage is sent. @param level The level of the LogMessage. @param message The message text. @param identifier The identifier of the LogMessage. */ LogMessage(LogLevel level, const IString& message, const std::string& identifier); /// Returns the level of the LogMessage. /** Returns the level of the LogMessage. @return The level of the LogMessage. */ LogLevel getLevel() const; protected: /// Sets the identifier of the LogMessage. /** The purpose of this method is to allow subclasses (in case there are any) to set the identifier of a LogMessage. @param identifier The identifier. */ void setIdentifier(std::string identifier); private: /// Composes a default identifier. /** This method composes a default identifier by combining the the process ID and the address of the Thread object corresponding to the calling thread. @return A default identifier. */ static std::string getDefaultIdentifier(); /// Sets the domain of the LogMessage /** This method sets the domain (origin) of the LogMessage. It is called by the Logger to which the LogMessage is sent. @param domain The domain. */ void setDomain(std::string domain); /// The time when the LogMessage was created. std::string time; /// The level (severity) of the LogMessage. LogLevel level; /// The domain (origin) of the LogMessage. std::string domain; /// An identifier that may be used for filtering. std::string identifier; /// The message text. IString message; /// Printing of LogMessages to ostreams. /** Output operator so that LogMessages can be printed conveniently by LogDestinations. */ friend std::ostream& operator<<(std::ostream& os, const LogMessage& message); /// The Logger class is a friend. /** The Logger class must have some privileges (e.g. ability to call the setDomain() method), therefore it is a friend. */ friend class Logger; }; /// A base class for log destinations. /** This class defines an interface for LogDestinations. LogDestination objects will typically contain synchronization mechanisms and should therefore never be copied. If setlocale() has been called with a supported locale, log messages will be logged in that locale. \headerfile Logger.h arc/Logger.h */ class LogDestination { public: /// Logs a LogMessage to this LogDestination. virtual void log(const LogMessage& message) = 0; virtual ~LogDestination() {} /// Set format for this log destination. void setFormat(const LogFormat& newformat); /// Returns currently assigned format LogFormat getFormat() const; /// Set format for any new log destination. static void setDefaultFormat(const LogFormat& newformat); /// Returns currently assigned default format static LogFormat getDefaultFormat(); /// Set a prefix for this log destination to be logged before messages. /** The prefix is prepended to messages in all formats except EmptyFormat. * \since Added in 4.0.0. */ void setPrefix(const std::string& prefix); /// Returns currently assignd prefix std::string getPrefix() const; protected: /// Default constructor. Protected since subclasses should be used instead. LogDestination(); private: /// Private copy constructor /** LogDestinations should never be copied, therefore the copy constructor is private. */ LogDestination(const LogDestination& unique); /// Private assignment operator /** LogDestinations should never be assigned, therefore the assignment operator is private. */ void operator=(const LogDestination& unique); /// Sets iword and pword for format and prefix friend std::ostream& operator<<(std::ostream& os, const LogDestination& dest); protected: /// A mutex for synchronization. /** This mutex is to be locked before a LogMessage is written and it is not unlocked until the entire message has been written and the stream flushed. This is done in order to prevent LogMessages to appear mixed in the stream and prevent race on modification of internal members. It is mutable to provide protection while reading members. */ mutable Glib::Mutex mutex; /// Format to use in this LogDestination. LogFormat format; /// Prefix to use in this log destination. /** * \since Added in 4.0.0. **/ std::string prefix; }; /// A class for logging to ostreams. /** This class is used for logging to ostreams (cout, cerr, files). It provides synchronization in order to prevent different LogMessages to appear mixed with each other in the stream. In order not to break the synchronization, LogStreams should never be copied. Therefore the copy constructor and assignment operator are private. Furthermore, it is important to keep a LogStream object as long as the Logger to which it has been registered. \headerfile Logger.h arc/Logger.h */ class LogStream : public LogDestination { public: /// Creates a LogStream connected to an ostream. /** Creates a LogStream connected to the specified ostream. In order not to break synchronization, it is important not to connect more than one LogStream object to a certain stream. @param destination The ostream to which to write LogMessages. */ LogStream(std::ostream& destination); /// Writes a LogMessage to the stream. /** This method writes a LogMessage to the ostream that is connected to this LogStream object. It is synchronized so that not more than one LogMessage can be written at a time. @param message The LogMessage to write. */ virtual void log(const LogMessage& message); private: /// Private copy constructor /** LogStreams should never be copied, therefore the copy constructor is private. */ LogStream(const LogStream& unique); /// Private assignment operator /** LogStreams should never be assigned, therefore the assignment operator is private. */ void operator=(const LogStream& unique); /// The ostream to which to write LogMessages /** This is the ostream to which LogMessages sent to this LogStream will be written. */ std::ostream& destination; }; /// A class for logging to files. /** This class is used for logging to files. It provides synchronization in order to prevent different LogMessages to appear mixed with each other in the stream. It is possible to limit size of created file. Whenever specified size is exceeded file is deleted and new one is created. Old files may be moved into backup files instead of being deleted. Those files have names same as initial file with additional number suffix - similar to those found in /var/log of many Unix-like systems. \headerfile Logger.h arc/Logger.h */ class LogFile : public LogDestination { public: /// Creates a LogFile connected to a file. /** Creates a LogFile connected to the file located at specified path. In order not to break synchronization, it is important not to connect more than one LogFile object to a certain file. If file does not exist it will be created. @param path The path to file to which to write LogMessages. */ LogFile(const std::string& path); /// Ordinary destructor ~LogFile(); /// Set maximal allowed size of file. /** Set maximal allowed size of file. This value is not obeyed exactly. Specified size may be exceeded by amount of one LogMessage. To disable limit specify -1. @param newsize Max size of log file. */ void setMaxSize(int newsize); /// Set number of backups to store. /** Set number of backups to store. When file size exceeds one specified with setMaxSize() file is closed and moved to one named path.1. If path.1 exists it is moved to path.2 and so on. Number of path.# files is one set in newbackup. @param newbackup Number of backup files. */ void setBackups(int newbackup); /// Set file reopen on every write. /** Set file reopen on every write. If set to true file is opened before writing every log record and closed afterward. @param newreopen If file to be reopened for every log record. */ void setReopen(bool newreopen); /// Reopen file if currently open. void Reopen(); /// Reopen all LogFile objects. static void ReopenAll(); /// Returns true if this instance is valid. operator bool(void); /// Returns true if this instance is invalid. bool operator!(void); /// Writes a LogMessage to the file. /** This method writes a LogMessage to the file that is connected to this LogFile object. If after writing size of file exceeds one set by setMaxSize() file is moved to backup and new one is created. @param message The LogMessage to write. */ virtual void log(const LogMessage& message); private: LogFile(void); LogFile(const LogFile& unique); void operator=(const LogFile& unique); void backup(void); std::string path; std::ofstream destination; int maxsize; int backups; bool reopen; std::list cache; int maxcachesize; Glib::Mutex file_mutex; }; class LoggerContextRef; /** \cond Container for internal logger configuration. \headerfile Logger.h arc/Logger.h */ class LoggerContext { friend class Logger; friend class LoggerContextRef; private: /// This counts how many threads are using this object. int usage_count; /// Protection for usage_count. Glib::Mutex mutex; /// A list of pointers to LogDestinations. std::list destinations; /// The threshold of Logger. LogLevel threshold; LoggerContext(LogLevel thr):usage_count(0),threshold(thr) { }; LoggerContext(const LoggerContext& ctx): usage_count(0),destinations(ctx.destinations),threshold(ctx.threshold) { }; ~LoggerContext(void); void Acquire(void); void Release(void); }; /** \endcond */ /// A logger class. /** This class defines a Logger to which LogMessages can be sent. Every Logger (except for the rootLogger) has a parent Logger. The domain of a Logger (a string that indicates the origin of LogMessages) is composed by adding a subdomain to the domain of its parent Logger. A Logger also has a threshold. Every LogMessage that have a level that is greater than or equal to the threshold is forwarded to any LogDestination connected to this Logger as well as to the parent Logger. Typical usage of the Logger class is to declare a global Logger object for each library/module/component to be used by all classes and methods there. Logger messages may be localised according to the current locale. Some locales are better supported than others. Example code for setting up logger in main(): @code // Set up stderr as a log stream Arc::LogStream logcerr(std::cerr); // Log message is prefixed by level only logcerr.setFormat(Arc::ShortFormat); // Add the stderr destination to the root logger Arc::Logger::getRootLogger().addDestination(logcerr); // Set the logging threshold to WARNING Arc::Logger::getRootLogger().setThreshold(Arc::WARNING); // Logger to use in main() - it inherits all properties from the root Logger Arc::Logger logger(Arc::Logger::getRootLogger(), "main"); // this message will not be logged since it is below the threshold logger.msg(Arc::INFO, "main started"); int i = 5; // This message will be logged logger.msg(Arc::ERROR, "Oops, an error occurred when i was %i", i); @endcode */ class Logger { public: /// The root Logger. /** This is the root Logger. It is an ancestor of any other Logger and always exists. */ //static Logger rootLogger; static Logger& getRootLogger(); /// Creates a logger. /** The threshold is inherited from its parent Logger. @param parent The parent Logger of the new Logger. @param subdomain The subdomain of the new logger. */ Logger(Logger& parent, const std::string& subdomain); /// Creates a logger. /** @param parent The parent Logger of the new Logger. @param subdomain The subdomain of the new logger. @param threshold The threshold of the new logger. */ Logger(Logger& parent, const std::string& subdomain, LogLevel threshold); /// Destroys a logger. ~Logger(); /// Adds a LogDestination. /** Adds a LogDestination to which to forward LogMessages sent to this logger (if they pass the threshold). Since LogDestinations should not be copied, the new LogDestination is passed by reference and a pointer to it is kept for later use. It is therefore important that the LogDestination passed to this Logger exists at least as long as the Logger itself. */ void addDestination(LogDestination& destination); /// Adds LogDestinations. /** See addDestination(LogDestination& destination). */ void addDestinations(const std::list& destinations); /// Set LogDestinations. /** A safe atomic way to remove and add LogDestinations. */ void setDestinations(const std::list& destinations); /// Obtains current LogDestinations. /** Returns list of pointers to LogDestination objects. Returned result refers directly to internal member of Logger instance. Hence it should not be used after this Logger is destroyed. */ const std::list& getDestinations(void) const; /// Removes all LogDestinations. void removeDestinations(void); /// Remove all LogDestinations and delete LogDestination objects. /** A LogDestination to not delete can be specified in the exclude parameter. * @param exclude Do not delete this destination */ void deleteDestinations(LogDestination* exclude=NULL); /// Sets the logging threshold. /** This method sets the threshold of the Logger. Any message sent to this Logger that has a level below this threshold will be discarded. @param threshold The threshold */ void setThreshold(LogLevel threshold); /// Sets the threshold for domain. /** This method sets the default threshold of the domain. All new loggers created with specified domain will have specified threshold set by default. The subdomains of all loggers in chain are matched against list of provided subdomains. @param threshold The threshold @param subdomains The subdomains of all loggers in chain */ static void setThresholdForDomain(LogLevel threshold, const std::list& subdomains); /// Sets the threshold for domain. /** This method sets the default threshold of the domain. All new loggers created with specified domain will have specified threshold set by default. The domain is composed of all subdomains of all loggers in chain by merging them with '.' as separator. @param threshold The threshold @param domain The domain of logger */ static void setThresholdForDomain(LogLevel threshold, const std::string& domain); /// Returns the threshold of this logger. LogLevel getThreshold() const; /// Creates per-thread context. /** Creates new context for this logger which becomes effective for operations initiated by this thread. All new threads started by this one will inherit new context. Context stores current threshold and pointers to destinations. Hence new context is identical to current one. One can modify new context using setThreshold(), removeDestinations() and addDestination(). All such operations will not affect old context. */ void setThreadContext(void); /// Sends a LogMessage. /** @param message The LogMessage to send. */ void msg(LogMessage message); /// Logs a message text. /** Logs a message text string at the specified LogLevel. This is a convenience method to save some typing. It simply creates a LogMessage and sends it to the other msg() methods. It is also possible to use msg() with multiple arguments and printf-style string formatting, for example @code logger.msg(INFO, "Operation no %i failed: %s", number, reason); @endcode @param level The level of the message. @param str The message text. */ void msg(LogLevel level, const std::string& str) { msg(LogMessage(level, IString(str))); } template void msg(LogLevel level, const std::string& str, const T0& t0) { msg(LogMessage(level, IString(str, t0))); } template void msg(LogLevel level, const std::string& str, const T0& t0, const T1& t1) { msg(LogMessage(level, IString(str, t0, t1))); } template void msg(LogLevel level, const std::string& str, const T0& t0, const T1& t1, const T2& t2) { msg(LogMessage(level, IString(str, t0, t1, t2))); } template void msg(LogLevel level, const std::string& str, const T0& t0, const T1& t1, const T2& t2, const T3& t3) { msg(LogMessage(level, IString(str, t0, t1, t2, t3))); } template void msg(LogLevel level, const std::string& str, const T0& t0, const T1& t1, const T2& t2, const T3& t3, const T4& t4) { msg(LogMessage(level, IString(str, t0, t1, t2, t3, t4))); } template void msg(LogLevel level, const std::string& str, const T0& t0, const T1& t1, const T2& t2, const T3& t3, const T4& t4, const T5& t5) { msg(LogMessage(level, IString(str, t0, t1, t2, t3, t4, t5))); } template void msg(LogLevel level, const std::string& str, const T0& t0, const T1& t1, const T2& t2, const T3& t3, const T4& t4, const T5& t5, const T6& t6) { msg(LogMessage(level, IString(str, t0, t1, t2, t3, t4, t5, t6))); } template void msg(LogLevel level, const std::string& str, const T0& t0, const T1& t1, const T2& t2, const T3& t3, const T4& t4, const T5& t5, const T6& t6, const T7& t7) { msg(LogMessage(level, IString(str, t0, t1, t2, t3, t4, t5, t6, t7))); } private: /// A private constructor. /** Every Logger (except the root logger) must have a parent, therefore the default constructor (which does not specify a parent) is private to prevent accidental use. It is only used when creating the root logger. */ Logger(); /// Private copy constructor /** Loggers should never be copied, therefore the copy constructor is private. */ Logger(const Logger& unique); /// Private assignment operator /** Loggers should never be assigned, therefore the assignment operator is private. */ void operator=(const Logger& unique); /// Returns the domain. /** This method returns the domain of this logger, i.e. the string that is attached to all LogMessages sent from this logger to indicate their origin. */ std::string getDomain(); /// Forwards a log message. /** This method is called by the msg() method and by child Loggers. It filters messages based on their level and forwards them to the parent Logger and any LogDestination that has been added to this Logger. @param message The message to send. */ void log(const LogMessage& message); /// A pointer to the parent of this logger. Logger *parent; /// The domain of this logger. std::string domain; /// Per-trhread storage id for context; std::string context_id; LoggerContext context; LoggerContext& getContext(void); /// Shared lock is used for processing log messages. /// Exclusive lock is for managing configuration, adding/removing destinations, etc. SharedMutex mutex; #define rootLoggerMagic (0xF6569201) static Logger *rootLogger; static std::map* defaultThresholds; static unsigned int rootLoggerMark; }; /** @} */ } // namespace Arc #define rootLogger getRootLogger() #define LOG(LGR, THR, FSTR, ...) { if ((LGR).getThreshold() >= (THR)(LGR).msg((THR), (FSTR), ...); } #endif // __ARC_LOGGER__