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;
 	}
 };