Mercurial > irccd
changeset 105:378fdc2c7b56
Irccd: cleanup logging system, #485
author | David Demelier <markand@malikania.fr> |
---|---|
date | Wed, 27 Apr 2016 12:38:18 +0200 |
parents | be4b9ed19a17 |
children | 8e7ddb14965e |
files | lib/irccd/logger.cpp lib/irccd/logger.hpp tests/js-logger/main.cpp |
diffstat | 3 files changed, 283 insertions(+), 186 deletions(-) [+] |
line wrap: on
line diff
--- a/lib/irccd/logger.cpp Tue Apr 26 22:09:02 2016 +0200 +++ b/lib/irccd/logger.cpp Wed Apr 27 12:38:18 2016 +0200 @@ -16,6 +16,8 @@ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ +#include <atomic> +#include <cassert> #include <cerrno> #include <cstring> #include <fstream> @@ -34,67 +36,67 @@ namespace log { -/* -------------------------------------------------------- - * Buffer -- output buffer (private) - * -------------------------------------------------------- */ +namespace { + +/* + * User definable options. + * ------------------------------------------------------------------ + */ + +std::atomic<bool> verbose{false}; +std::unique_ptr<Interface> iface{new Console}; -/** - * @class LoggerBuffer - * @brief This class is a internal buffer for the Logger streams +/* + * Buffer -- output buffer. + * ------------------------------------------------------------------ + * + * This class inherits from std::stringbuf and writes the messages to the specified interface function which is one of + * info, warning and debug. */ + class Buffer : public std::stringbuf { +public: + enum Level { + Debug, + Info, + Warning + }; + private: - Interface *m_interface{nullptr}; Level m_level; + void debug(const std::string &line) + { + /* Print only in debug mode, the buffer is flushed anyway */ +#if !defined(NDEBUG) + iface->debug(line); +#else + (void)line; +#endif + } + + void info(const std::string &line) + { + /* Print only if verbose, the buffer will be flushed anyway. */ + if (verbose) { + iface->info(line); + } + } + + void warning(const std::string &line) + { + iface->warning(line); + } + public: - /** - * Create the buffer with the specified level. - * - * @param level the level - */ inline Buffer(Level level) noexcept : m_level(level) { + assert(level >= Debug && level <= Warning); } - /** - * Update the underlying interface. - * - * @param iface is a non-owning pointer to the new interface - */ - inline void setInterface(Interface *iface) noexcept - { - m_interface = iface; - } - - /** - * Sync the buffer by calling the interface if set. - * - * This function split the buffer line per line and remove it before - * calling the appropriate interface function. - */ virtual int sync() override { -#if defined(NDEBUG) - /* - * Debug is disabled, don't call interface->write() but don't - * forget to flush the buffer. - */ - if (m_level == Level::Debug) { - str(""); - - return 0; - } -#endif - - /* Verbose is disabled? Don't show and flush the buffer too. */ - if (m_level == Level::Info && !isVerbose()) { - str(""); - - return 0; - } - std::string buffer = str(); std::string::size_type pos; @@ -104,8 +106,19 @@ /* Remove this line */ buffer.erase(buffer.begin(), buffer.begin() + pos + 1); - if (m_interface) - m_interface->write(m_level, line); + switch (m_level) { + case Level::Debug: + debug(line); + break; + case Level::Info: + info(line); + break; + case Level::Warning: + warning(line); + break; + default: + break; + } } str(buffer); @@ -114,71 +127,94 @@ } }; -/* -------------------------------------------------------- - * Local variables - * -------------------------------------------------------- */ +/* + * Local variables. + * ------------------------------------------------------------------ + */ -namespace { - -/* Generic interface for all outputs */ -std::unique_ptr<Interface> iface; +/* Information buffer */ +Buffer buffer_info{Buffer::Info}; -/* Internal buffers */ -Buffer bufferInfo(Level::Info); -Buffer bufferWarning(Level::Warning); -Buffer bufferDebug(Level::Debug); +/* Warning buffer */ +Buffer buffer_warning{Buffer::Warning}; -/* Stream outputs */ -std::ostream streamInfo(&bufferInfo); -std::ostream streamWarning(&bufferWarning); -std::ostream streamDebug(&bufferDebug); +/* Debug buffer */ +Buffer buffer_debug{Buffer::Debug}; -/* Options */ -bool verbose(false); +/* Stream outputs. */ +std::ostream stream_info(&buffer_info); +std::ostream stream_warning(&buffer_warning); +std::ostream stream_debug(&buffer_debug); } // !namespace -/* -------------------------------------------------------- +/* * Console - * -------------------------------------------------------- */ + * ------------------------------------------------------------------ + */ -void Console::write(Level level, const std::string &line) noexcept +void Console::info(const std::string &line) { - if (level == Level::Warning) - std::cerr << line << std::endl; - else - std::cout << line << std::endl; + std::cout << line << std::endl; } -/* -------------------------------------------------------- +void Console::warning(const std::string &line) +{ + std::cerr << line << std::endl; +} + +void Console::debug(const std::string &line) +{ + std::cout << line << std::endl; +} + +/* * File - * -------------------------------------------------------- */ + * ------------------------------------------------------------------ + */ File::File(std::string normal, std::string errors) - : m_outputNormal(std::move(normal)) - , m_outputError(std::move(errors)) + : m_output_normal(std::move(normal)) + , m_output_error(std::move(errors)) { } -void File::write(Level level, const std::string &line) noexcept +void File::info(const std::string &line) { - std::string &path = (level == Level::Warning) ? m_outputError : m_outputNormal; - std::ofstream output(path, std::ofstream::out | std::ofstream::app); + std::ofstream(m_output_normal, std::ofstream::out | std::ofstream::app) << line << std::endl; +} - output << line << std::endl; +void File::warning(const std::string &line) +{ + std::ofstream(m_output_error, std::ofstream::out | std::ofstream::app) << line << std::endl; } -/* -------------------------------------------------------- +void File::debug(const std::string &line) +{ + std::ofstream(m_output_normal, std::ofstream::out | std::ofstream::app) << line << std::endl; +} + +/* * Silent - * -------------------------------------------------------- */ + * ------------------------------------------------------------------ + */ -void Silent::write(Level, const std::string &) noexcept +void Silent::info(const std::string &) { } -/* -------------------------------------------------------- +void Silent::warning(const std::string &) +{ +} + +void Silent::debug(const std::string &) +{ +} + +/* * Syslog - * -------------------------------------------------------- */ + * ------------------------------------------------------------------ + */ #if defined(HAVE_SYSLOG) @@ -192,54 +228,60 @@ closelog(); } -void Syslog::write(Level level, const std::string &line) noexcept +void Syslog::info(const std::string &line) { - int syslogLevel; + syslog(LOG_INFO | LOG_USER, "%s", line.c_str()); +} - switch (level) { - case Level::Warning: - syslogLevel = LOG_WARNING; - break; - case Level::Debug: - syslogLevel = LOG_DEBUG; - break; - case Level::Info: - /* FALLTHROUGH */ - default: - syslogLevel = LOG_INFO; - break; - } +void Syslog::warning(const std::string &line) +{ + syslog(LOG_WARNING | LOG_USER, "%s", line.c_str()); +} - syslog(syslogLevel | LOG_USER, "%s", line.c_str()); +void Syslog::debug(const std::string &line) +{ + syslog(LOG_DEBUG | LOG_USER, "%s", line.c_str()); } #endif // !HAVE_SYSLOG -/* -------------------------------------------------------- +/* * Functions - * -------------------------------------------------------- */ + * ------------------------------------------------------------------ + */ -void setInterface(std::unique_ptr<Interface> ifaceValue) noexcept +void setInterface(std::unique_ptr<Interface> newiface) noexcept { - iface = std::move(ifaceValue); - bufferInfo.setInterface(iface.get()); - bufferWarning.setInterface(iface.get()); - bufferDebug.setInterface(iface.get()); + assert(newiface); + + iface = std::move(newiface); } -std::ostream &info() noexcept +std::ostream &info(const std::string &message) { - return streamInfo; + if (!message.empty()) { + stream_info << message << std::endl; + } + + return stream_info; } -std::ostream &warning() noexcept +std::ostream &warning(const std::string &message) { - return streamWarning; + if (!message.empty()) { + stream_warning << message << std::endl; + } + + return stream_warning; } -std::ostream &debug() noexcept +std::ostream &debug(const std::string &message) { - return streamDebug; + if (!message.empty()) { + stream_debug << message << std::endl; + } + + return stream_debug; } bool isVerbose() noexcept
--- a/lib/irccd/logger.hpp Tue Apr 26 22:09:02 2016 +0200 +++ b/lib/irccd/logger.hpp Wed Apr 27 12:38:18 2016 +0200 @@ -34,26 +34,15 @@ namespace log { -/** - * \enum Level - * \brief Which level of warning +/* + * Interface -- abstract logging interface + * ------------------------------------------------------------------ */ -enum class Level { - Info, //!< Standard information (disabled if verbose is false) - Warning, //!< Warning (always shown) - Debug //!< Debug message (only if compiled in debug mode) -}; - -/* -------------------------------------------------------- - * Interface -- abstract logging interface - * -------------------------------------------------------- */ /** - * \class Interface - * \brief Interface to implement new logger mechanisms + * \brief Interface to implement new logger mechanisms. * - * Derive from this class and use Logger::setInterface() to change logging - * system. + * Derive from this class and use log::setInterface() to change logging system. * * \see File * \see Console @@ -63,50 +52,78 @@ class Interface { public: /** - * Write the line to the logs. The line to write will never contains - * trailing new line character. + * Write a information message. + * + * The function is called only if verbose is true. + * + * \param data the data + * \see log::info + */ + virtual void info(const std::string &line) = 0; + + /** + * Write an error message. + * + * This function is always called. * - * \param level the level - * \param line the line without trailing \n - */ - virtual void write(Level level, const std::string &line) noexcept = 0; + * \param data the data + * \see log::warning + */ + virtual void warning(const std::string &line) = 0; + + /** + * Write a debug message. + * + * This function is called only if NDEBUG is not defined. + * + * \param data the data + * \see log::debug + */ + virtual void debug(const std::string &line) = 0; }; -/* -------------------------------------------------------- +/* * Console -- logs to console - * -------------------------------------------------------- */ + * ------------------------------------------------------------------ + */ /** - * \class Console - * \brief Logger implementation for console output + * \brief Logger implementation for console output using std::cout and std::cerr. */ class Console : public Interface { public: /** - * \copydoc Interface::write + * \copydoc Interface::info + */ + void info(const std::string &line) override; + + /** + * \copydoc Interface::warning */ - void write(Level level, const std::string &line) noexcept override; + void warning(const std::string &line) override; + + /** + * \copydoc Interface::debug + */ + void debug(const std::string &line) override; }; -/* -------------------------------------------------------- +/* * File -- logs to a file - * -------------------------------------------------------- */ + * ------------------------------------------------------------------ + */ /** - * \class File - * \brief Output to a file + * \brief Output to a files. */ class File : public Interface { private: - std::string m_outputNormal; - std::string m_outputError; + std::string m_output_normal; + std::string m_output_error; public: /** - * Outputs to files. Info and Debug are written in normal and Warnings - * in errors. - * - * The same path can be used for all levels. + * Outputs to files. * * \param normal the path to the normal logs * \param errors the path to the errors logs @@ -114,38 +131,58 @@ File(std::string normal, std::string errors); /** - * \copydoc Interface::write + * \copydoc Interface::info + */ + void info(const std::string &line) override; + + /** + * \copydoc Interface::warning */ - void write(Level level, const std::string &line) noexcept override; + void warning(const std::string &line) override; + + /** + * \copydoc Interface::debug + */ + void debug(const std::string &line) override; }; -/* -------------------------------------------------------- +/* * Silent -- disable all logs - * -------------------------------------------------------- */ + * ------------------------------------------------------------------ + */ /** - * \class Silent - * \brief Use to disable logs + * \brief Use to disable logs. * * Useful for unit tests when some classes may emits log. */ class Silent : public Interface { public: /** - * \copydoc Interface::write + * \copydoc Interface::info + */ + void info(const std::string &line) override; + + /** + * \copydoc Interface::warning */ - void write(Level level, const std::string &line) noexcept override; + void warning(const std::string &line) override; + + /** + * \copydoc Interface::debug + */ + void debug(const std::string &line) override; }; -/* -------------------------------------------------------- +/* * Syslog -- system logger - * -------------------------------------------------------- */ + * ------------------------------------------------------------------ + */ #if defined(HAVE_SYSLOG) /** - * \class Syslog - * \brief Implements logger into syslog + * \brief Implements logger into syslog. */ class Syslog : public Interface { public: @@ -160,16 +197,27 @@ ~Syslog(); /** - * \copydoc Interface::write + * \copydoc Interface::info + */ + void info(const std::string &line) override; + + /** + * \copydoc Interface::warning */ - void write(Level level, const std::string &line) noexcept override; + void warning(const std::string &line) override; + + /** + * \copydoc Interface::debug + */ + void debug(const std::string &line) override; }; #endif // !HAVE_SYSLOG -/* -------------------------------------------------------- +/* * Functions - * -------------------------------------------------------- */ + * ------------------------------------------------------------------ + */ /** * Update the logger interface. @@ -181,25 +229,34 @@ /** * Get the stream for informational messages. * + * If message is specified, a new line character is appended. + * + * \param message the optional message to write * \return the stream * \note Has no effect if verbose is set to false. */ -std::ostream &info() noexcept; +std::ostream &info(const std::string &message = ""); /** * Get the stream for warnings. * + * If message is specified, a new line character is appended. + * + * \param message the optional message to write * \return the stream */ -std::ostream &warning() noexcept; +std::ostream &warning(const std::string &message = ""); /** * Get the stream for debug messages. * + * If message is specified, a new line character is appended. + * + * \param message the optional message to write * \return the stream * \note Has no effect if compiled in release mode. */ -std::ostream &debug() noexcept; +std::ostream &debug(const std::string &message = ""); /** * Tells if verbose is enabled.
--- a/tests/js-logger/main.cpp Tue Apr 26 22:09:02 2016 +0200 +++ b/tests/js-logger/main.cpp Wed Apr 27 12:38:18 2016 +0200 @@ -36,21 +36,19 @@ class TestLogger : public log::Interface { public: - void write(log::Level level, const std::string &line) noexcept override + void info(const std::string &line) override + { + lineInfo = line; + } + + void warning(const std::string &line) override { - switch (level) { - case log::Level::Info: - lineInfo = line; - break; - case log::Level::Warning: - lineWarning = line; - break; - case log::Level::Debug: - lineDebug = line; - break; - default: - break; - } + lineWarning = line; + } + + void debug(const std::string &line) override + { + lineDebug = line; } };