changeset 599:e531f04507aa

Irccd: move logger into libirccd, closes #740
author David Demelier <markand@malikania.fr>
date Wed, 06 Dec 2017 21:32:10 +0100
parents a3eeb5e9c482
children 080800ce4f1e
files irccd/main.cpp irccdctl/main.cpp libcommon/CMakeLists.txt libcommon/irccd/logger.cpp libcommon/irccd/logger.hpp libirccd-js/irccd/js/logger_jsapi.cpp libirccd-js/irccd/js/timer_jsapi.cpp libirccd-test/irccd/command_test.hpp libirccd-test/irccd/js_test.hpp libirccd-test/irccd/plugin_test.cpp libirccd/CMakeLists.txt libirccd/irccd/daemon/irccd.cpp libirccd/irccd/daemon/irccd.hpp libirccd/irccd/daemon/logger.cpp libirccd/irccd/daemon/logger.hpp libirccd/irccd/daemon/plugin_service.cpp libirccd/irccd/daemon/rule_service.cpp libirccd/irccd/daemon/rule_service.hpp libirccd/irccd/daemon/server.cpp libirccd/irccd/daemon/server_service.cpp libirccd/irccd/daemon/transport_service.cpp tests/src/logger-jsapi/main.cpp tests/src/logger/main.cpp tests/src/plugin-logger/main.cpp tests/src/plugin-plugin/main.cpp tests/src/rules/main.cpp
diffstat 26 files changed, 867 insertions(+), 875 deletions(-) [+]
line wrap: on
line diff
--- a/irccd/main.cpp	Wed Dec 06 14:12:57 2017 +0100
+++ b/irccd/main.cpp	Wed Dec 06 21:32:10 2017 +0100
@@ -33,7 +33,6 @@
 #include <csignal>
 #include <iostream>
 
-#include <irccd/logger.hpp>
 #include <irccd/options.hpp>
 #include <irccd/string_util.hpp>
 #include <irccd/system.hpp>
@@ -41,6 +40,7 @@
 #include <irccd/daemon/command_service.hpp>
 #include <irccd/daemon/config.hpp>
 #include <irccd/daemon/irccd.hpp>
+#include <irccd/daemon/logger.hpp>
 #include <irccd/daemon/plugin_service.hpp>
 #include <irccd/daemon/rule_service.hpp>
 #include <irccd/daemon/server_service.hpp>
@@ -109,7 +109,7 @@
     sys::set_program_name("irccd");
 
     // Default logging to console.
-    log::set_verbose(false);
+    instance->log().set_verbose(false);
 
     -- argc;
     ++ argv;
@@ -143,10 +143,10 @@
                 version(result);
                 // NOTREACHED
             if (pair.first == "-v" || pair.first == "--verbose")
-                log::set_verbose(true);
+                instance->log().set_verbose(true);
         }
     } catch (const std::exception& ex) {
-        log::warning() << "irccd: " << ex.what() << std::endl;
+        instance->log().warning() << "irccd: " << ex.what() << std::endl;
         usage();
     }
 
@@ -171,14 +171,15 @@
 {
     using namespace irccd;
 
-    init(argc, argv);
-
     boost::asio::io_service service;
     boost::asio::signal_set sigs(service, SIGINT, SIGTERM);
 
+    instance = std::make_unique<class irccd>(service);
+
+    init(argc, argv);
+
     auto options = parse(argc, argv);
 
-    instance = std::make_unique<class irccd>(service);
     instance->commands().add(std::make_unique<plugin_config_command>());
     instance->commands().add(std::make_unique<plugin_info_command>());
     instance->commands().add(std::make_unique<plugin_list_command>());
@@ -230,7 +231,7 @@
         instance->set_config(open(options));
         instance->load();
     } catch (const std::exception& ex) {
-        log::warning() << "abort: " << ex.what() << std::endl;
+        std::cerr << "abort: " << ex.what() << std::endl;
         return 1;
     }
 
--- a/irccdctl/main.cpp	Wed Dec 06 14:12:57 2017 +0100
+++ b/irccdctl/main.cpp	Wed Dec 06 21:32:10 2017 +0100
@@ -25,7 +25,6 @@
 
 #include <irccd/ini.hpp>
 #include <irccd/json_util.hpp>
-#include <irccd/logger.hpp>
 #include <irccd/options.hpp>
 #include <irccd/string_util.hpp>
 #include <irccd/system.hpp>
@@ -86,6 +85,9 @@
 
 #endif
 
+// Global options.
+bool verbose = true;
+
 // Connection to instance.
 std::unique_ptr<connection> conn;
 std::unique_ptr<controller> ctl;
@@ -213,10 +215,10 @@
  */
 void read_general(const ini::section& sc)
 {
-    auto verbose = sc.find("verbose");
+    auto value = sc.find("verbose");
 
-    if (verbose != sc.end())
-        log::set_verbose(string_util::is_boolean(verbose->value()));
+    if (value != sc.end())
+        verbose = string_util::is_boolean(value->value());
 }
 
 /*
@@ -276,7 +278,7 @@
             }
         }
     } catch (const std::exception &ex) {
-        log::warning() << path << ": " << ex.what() << std::endl;
+        std::cerr << path << ": " << ex.what() << std::endl;
     }
 }
 
@@ -393,9 +395,9 @@
             // NOTREACHED
 
         if (result.count("-v") != 0 || result.count("--verbose") != 0)
-            log::set_verbose(true);
+            verbose = true;
     } catch (const std::exception& ex) {
-        log::warning() << "irccdctl: " << ex.what() << std::endl;
+        std::cerr << "irccdctl: " << ex.what() << std::endl;
         usage();
     }
 
@@ -512,10 +514,11 @@
         if (code)
             throw boost::system::system_error(code);
 
-        log::info(string_util::sprintf("connected to irccd %d.%d.%d",
-            json_util::to_int(info["major"]),
-            json_util::to_int(info["minor"]),
-            json_util::to_int(info["patch"])));
+        if (verbose)
+            std::cout << string_util::sprintf("connected to irccd %d.%d.%d\n",
+                json_util::to_int(info["major"]),
+                json_util::to_int(info["minor"]),
+                json_util::to_int(info["patch"]));
 
         connected = true;
     });
@@ -580,7 +583,7 @@
             }
         }
     } catch (const std::exception& ex) {
-        irccd::log::warning() << "abort: " << ex.what() << std::endl;
+        std::cerr << "abort: " << ex.what() << std::endl;
         return 1;
     }
 
@@ -589,7 +592,7 @@
         // NOTREACHED
 
     if (!irccd::ctl::ctl) {
-        irccd::log::warning("abort: no connection specified");
+        std::cerr << "abort: no connection specified" << std::endl;
         return 1;
     }
 
--- a/libcommon/CMakeLists.txt	Wed Dec 06 14:12:57 2017 +0100
+++ b/libcommon/CMakeLists.txt	Wed Dec 06 21:32:10 2017 +0100
@@ -25,7 +25,6 @@
     ${libcommon_SOURCE_DIR}/irccd/fs_util.hpp
     ${libcommon_SOURCE_DIR}/irccd/ini.hpp
     ${libcommon_SOURCE_DIR}/irccd/json_util.hpp
-    ${libcommon_SOURCE_DIR}/irccd/logger.hpp
     ${libcommon_SOURCE_DIR}/irccd/network_stream.hpp
     ${libcommon_SOURCE_DIR}/irccd/options.hpp
     ${libcommon_SOURCE_DIR}/irccd/string_util.hpp
@@ -38,7 +37,6 @@
     SOURCES
     ${libcommon_SOURCE_DIR}/irccd/ini.cpp
     ${libcommon_SOURCE_DIR}/irccd/json_util.cpp
-    ${libcommon_SOURCE_DIR}/irccd/logger.cpp
     ${libcommon_SOURCE_DIR}/irccd/options.cpp
     ${libcommon_SOURCE_DIR}/irccd/string_util.cpp
     ${libcommon_SOURCE_DIR}/irccd/system.cpp
--- a/libcommon/irccd/logger.cpp	Wed Dec 06 14:12:57 2017 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,299 +0,0 @@
-/*
- * logger.cpp -- irccd logging
- *
- * Copyright (c) 2013-2017 David Demelier <markand@malikania.fr>
- *
- * Permission to use, copy, modify, and/or distribute this software for any
- * purpose with or without fee is hereby granted, provided that the above
- * copyright notice and this permission notice appear in all copies.
- *
- * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
- * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
- * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
- * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
- * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
- */
-
-#include <atomic>
-#include <cassert>
-#include <cerrno>
-#include <cstring>
-#include <fstream>
-#include <iostream>
-#include <stdexcept>
-#include <streambuf>
-
-#include "logger.hpp"
-#include "system.hpp"
-
-#if defined(HAVE_SYSLOG)
-#  include <syslog.h>
-#endif // !HAVE_SYSLOG
-
-namespace irccd {
-
-namespace log {
-
-namespace {
-
-/*
- * User definable options.
- * ------------------------------------------------------------------
- */
-
-std::atomic<bool> verbose{false};
-std::unique_ptr<logger> use_iface{new console_logger};
-std::unique_ptr<filter> use_filter{new filter};
-
-/*
- * 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 class level {
-        debug,
-        info,
-        warning
-    };
-
-private:
-    level level_;
-
-    void debug(std::string line)
-    {
-        // Print only in debug mode, the buffer is flushed anyway.
-#if !defined(NDEBUG)
-        use_iface->debug(use_filter->pre_debug(std::move(line)));
-#else
-        (void)line;
-#endif
-    }
-
-    void info(std::string line)
-    {
-        // Print only if verbose, the buffer will be flushed anyway.
-        if (verbose)
-            use_iface->info(use_filter->pre_info(std::move(line)));
-    }
-
-    void warning(std::string line)
-    {
-        use_iface->warning(use_filter->pre_warning(std::move(line)));
-    }
-
-public:
-    inline buffer(level level) noexcept
-        : level_(level)
-    {
-        assert(level >= level::debug && level <= level::warning);
-    }
-
-    virtual int sync() override
-    {
-        std::string buffer = str();
-        std::string::size_type pos;
-
-        while ((pos = buffer.find("\n")) != std::string::npos) {
-            auto line = buffer.substr(0, pos);
-
-            // Remove this line.
-            buffer.erase(buffer.begin(), buffer.begin() + pos + 1);
-
-            switch (level_) {
-            case level::debug:
-                debug(std::move(line));
-                break;
-            case level::info:
-                info(std::move(line));
-                break;
-            case level::warning:
-                warning(std::move(line));
-                break;
-            default:
-                break;
-            }
-        }
-
-        str(buffer);
-
-        return 0;
-    }
-};
-
-/*
- * Local variables.
- * ------------------------------------------------------------------
- */
-
-// Buffers.
-buffer buffer_info{buffer::level::info};
-buffer buffer_warning{buffer::level::warning};
-buffer buffer_debug{buffer::level::debug};
-
-// Stream outputs.
-std::ostream stream_info(&buffer_info);
-std::ostream stream_warning(&buffer_warning);
-std::ostream stream_debug(&buffer_debug);
-
-} // !namespace
-
-/*
- * console_logger
- * ------------------------------------------------------------------
- */
-
-void console_logger::info(const std::string& line)
-{
-    std::cout << line << std::endl;
-}
-
-void console_logger::warning(const std::string& line)
-{
-    std::cerr << line << std::endl;
-}
-
-void console_logger::debug(const std::string& line)
-{
-    std::cout << line << std::endl;
-}
-
-/*
- * file_logger
- * ------------------------------------------------------------------
- */
-
-file_logger::file_logger(std::string normal, std::string errors)
-    : output_normal_(std::move(normal))
-    , output_error_(std::move(errors))
-{
-}
-
-void file_logger::info(const std::string& line)
-{
-    std::ofstream(output_normal_, std::ofstream::out | std::ofstream::app) << line << std::endl;
-}
-
-void file_logger::warning(const std::string& line)
-{
-    std::ofstream(output_error_, std::ofstream::out | std::ofstream::app) << line << std::endl;
-}
-
-void file_logger::debug(const std::string& line)
-{
-    std::ofstream(output_normal_, std::ofstream::out | std::ofstream::app) << line << std::endl;
-}
-
-/*
- * silent_logger
- * ------------------------------------------------------------------
- */
-
-void silent_logger::info(const std::string&)
-{
-}
-
-void silent_logger::warning(const std::string&)
-{
-}
-
-void silent_logger::debug(const std::string&)
-{
-}
-
-/*
- * syslog_logger
- * ------------------------------------------------------------------
- */
-
-#if defined(HAVE_SYSLOG)
-
-syslog_logger::syslog_logger()
-{
-    openlog("irccd", LOG_PID, LOG_DAEMON);
-}
-
-syslog_logger::~syslog_logger()
-{
-    closelog();
-}
-
-void syslog_logger::info(const std::string& line)
-{
-    syslog(LOG_INFO | LOG_USER, "%s", line.c_str());
-}
-
-void syslog_logger::warning(const std::string& line)
-{
-    syslog(LOG_WARNING | LOG_USER, "%s", line.c_str());
-}
-
-void syslog_logger::debug(const std::string& line)
-{
-    syslog(LOG_DEBUG | LOG_USER, "%s", line.c_str());
-}
-
-#endif // !HAVE_SYSLOG
-
-/*
- * Functions
- * ------------------------------------------------------------------
- */
-
-void set_logger(std::unique_ptr<logger> new_iface) noexcept
-{
-    assert(new_iface);
-
-    use_iface = std::move(new_iface);
-}
-
-void set_filter(std::unique_ptr<filter> newfilter) noexcept
-{
-    assert(newfilter);
-
-    use_filter = std::move(newfilter);
-}
-
-std::ostream& info(const std::string& message)
-{
-    if (!message.empty())
-        stream_info << message << std::endl;
-
-    return stream_info;
-}
-
-std::ostream& warning(const std::string& message)
-{
-    if (!message.empty())
-        stream_warning << message << std::endl;
-
-    return stream_warning;
-}
-
-std::ostream& debug(const std::string& message)
-{
-    if (!message.empty())
-        stream_debug << message << std::endl;
-
-    return stream_debug;
-}
-
-bool is_verbose() noexcept
-{
-    return verbose;
-}
-
-void set_verbose(bool mode) noexcept
-{
-    verbose = mode;
-}
-
-} // !log
-
-} // !irccd
--- a/libcommon/irccd/logger.hpp	Wed Dec 06 14:12:57 2017 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,345 +0,0 @@
-/*
- * logger.hpp -- irccd logging
- *
- * Copyright (c) 2013-2017 David Demelier <markand@malikania.fr>
- *
- * Permission to use, copy, modify, and/or distribute this software for any
- * purpose with or without fee is hereby granted, provided that the above
- * copyright notice and this permission notice appear in all copies.
- *
- * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
- * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
- * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
- * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
- * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
- */
-
-#ifndef IRCCD_LOGGER_HPP
-#define IRCCD_LOGGER_HPP
-
-/**
- * \file logger.hpp
- * \brief Logging facilities.
- */
-
-#include <memory>
-#include <sstream>
-#include <utility>
-
-#include "sysconfig.hpp"
-
-namespace irccd {
-
-namespace log {
-
-/*
- * logger -- abstract logging interface
- * ------------------------------------------------------------------
- */
-
-/**
- * \brief Interface to implement new logger mechanisms.
- *
- * Derive from this class and use log::set_logger() to change logging system.
- *
- * \see file_logger
- * \see console_logger
- * \see syslog_logger
- * \see silent_logger
- */
-class logger {
-public:
-    /**
-     * Virtual destructor defaulted.
-     */
-    virtual ~logger() = default;
-
-    /**
-     * Write a debug message.
-     *
-     * This function is called only if NDEBUG is not defined.
-     *
-     * \param line the data
-     * \see log::debug
-     */
-    virtual void debug(const std::string& line) = 0;
-
-    /**
-     * Write a information message.
-     *
-     * The function is called only if verbose is true.
-     *
-     * \param line the data
-     * \see log::info
-     */
-    virtual void info(const std::string& line) = 0;
-
-    /**
-     * Write an error message.
-     *
-     * This function is always called.
-     *
-     * \param line the data
-     * \see log::warning
-     */
-    virtual void warning(const std::string& line) = 0;
-};
-
-/*
- * filter -- modify messages before printing
- * ------------------------------------------------------------------
- */
-
-/**
- * \brief Filter messages before printing them.
- *
- * Derive from this class and use log::setFilter.
- */
-class filter {
-public:
-    /**
-     * Virtual destructor defaulted.
-     */
-    virtual ~filter() = default;
-
-    /**
-     * Update the debug message.
-     *
-     * \param input the message
-     * \return the updated message
-     */
-    virtual std::string pre_debug(std::string input) const
-    {
-        return input;
-    }
-
-    /**
-     * Update the information message.
-     *
-     * \param input the message
-     * \return the updated message
-     */
-    virtual std::string pre_info(std::string input) const
-    {
-        return input;
-    }
-
-    /**
-     * Update the warning message.
-     *
-     * \param input the message
-     * \return the updated message
-     */
-    virtual std::string pre_warning(std::string input) const
-    {
-        return input;
-    }
-};
-
-/*
- * console_logger -- logs to console
- * ------------------------------------------------------------------
- */
-
-/**
- * \brief Logger implementation for console output using std::cout and
- *        std::cerr.
- */
-class console_logger : public logger {
-public:
-    /**
-     * \copydoc logger::debug
-     */
-    void debug(const std::string& line) override;
-
-    /**
-     * \copydoc logger::info
-     */
-    void info(const std::string& line) override;
-
-    /**
-     * \copydoc logger::warning
-     */
-    void warning(const std::string& line) override;
-};
-
-/*
- * file_logger -- logs to a file
- * ------------------------------------------------------------------
- */
-
-/**
- * \brief Output to a files.
- */
-class file_logger : public logger {
-private:
-    std::string output_normal_;
-    std::string output_error_;
-
-public:
-    /**
-     * Outputs to files.
-     *
-     * \param normal the path to the normal logs
-     * \param errors the path to the errors logs
-     */
-    file_logger(std::string normal, std::string errors);
-
-    /**
-     * \copydoc logger::debug
-     */
-    void debug(const std::string& line) override;
-
-    /**
-     * \copydoc logger::info
-     */
-    void info(const std::string& line) override;
-
-    /**
-     * \copydoc logger::warning
-     */
-    void warning(const std::string& line) override;
-};
-
-/*
- * silent_logger -- disable all logs
- * ------------------------------------------------------------------
- */
-
-/**
- * \brief Use to disable logs.
- *
- * Useful for unit tests when some classes may emits log.
- */
-class silent_logger : public logger {
-public:
-    /**
-     * \copydoc logger::debug
-     */
-    void debug(const std::string& line) override;
-
-    /**
-     * \copydoc logger::info
-     */
-    void info(const std::string& line) override;
-
-    /**
-     * \copydoc logger::warning
-     */
-    void warning(const std::string& line) override;
-};
-
-/*
- * syslog_logger -- system logger
- * ------------------------------------------------------------------
- */
-
-#if defined(HAVE_SYSLOG)
-
-/**
- * \brief Implements logger into syslog.
- */
-class syslog_logger : public logger {
-public:
-    /**
-     * Open the syslog.
-     */
-    syslog_logger();
-
-    /**
-     * Close the syslog.
-     */
-    ~syslog_logger();
-
-    /**
-     * \copydoc logger::debug
-     */
-    void debug(const std::string& line) override;
-
-    /**
-     * \copydoc logger::info
-     */
-    void info(const std::string& line) override;
-
-    /**
-     * \copydoc logger::warning
-     */
-    void warning(const std::string& line) override;
-};
-
-#endif // !HAVE_SYSLOG
-
-/*
- * Functions
- * ------------------------------------------------------------------
- */
-
-/**
- * Update the logger interface.
- *
- * \pre iface must not be null
- * \param iface the new interface
- */
-IRCCD_EXPORT void set_logger(std::unique_ptr<logger> iface) noexcept;
-
-/**
- * Set an optional filter.
- *
- * \pre filter must not be null
- * \param filter the filter
- */
-IRCCD_EXPORT void set_filter(std::unique_ptr<filter> filter) noexcept;
-
-/**
- * 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.
- */
-IRCCD_EXPORT 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
- */
-IRCCD_EXPORT 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.
- */
-IRCCD_EXPORT std::ostream& debug(const std::string& message = "");
-
-/**
- * Tells if verbose is enabled.
- *
- * \return true if enabled
- */
-IRCCD_EXPORT bool is_verbose() noexcept;
-
-/**
- * Set the verbosity mode.
- *
- * \param mode the new mode
- */
-IRCCD_EXPORT void set_verbose(bool mode) noexcept;
-
-} // !log
-
-} // !irccd
-
-#endif // !IRCCD_LOGGER_HPP
--- a/libirccd-js/irccd/js/logger_jsapi.cpp	Wed Dec 06 14:12:57 2017 +0100
+++ b/libirccd-js/irccd/js/logger_jsapi.cpp	Wed Dec 06 21:32:10 2017 +0100
@@ -16,8 +16,10 @@
  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  */
 
-#include <irccd/logger.hpp>
+#include <irccd/daemon/logger.hpp>
+#include <irccd/daemon/irccd.hpp>
 
+#include "irccd_jsapi.hpp"
 #include "js_plugin.hpp"
 #include "logger_jsapi.hpp"
 #include "plugin_jsapi.hpp"
@@ -44,7 +46,7 @@
  */
 duk_ret_t info(duk_context* ctx)
 {
-    return print(ctx, log::info());
+    return print(ctx, dukx_get_irccd(ctx).log().info());
 }
 
 /*
@@ -58,7 +60,7 @@
  */
 duk_ret_t warning(duk_context* ctx)
 {
-    return print(ctx, log::warning());
+    return print(ctx, dukx_get_irccd(ctx).log().warning());
 }
 
 /*
@@ -72,7 +74,7 @@
  */
 duk_ret_t debug(duk_context* ctx)
 {
-    return print(ctx, log::debug());
+    return print(ctx, dukx_get_irccd(ctx).log().debug());
 }
 
 const duk_function_list_entry functions[] = {
--- a/libirccd-js/irccd/js/timer_jsapi.cpp	Wed Dec 06 14:12:57 2017 +0100
+++ b/libirccd-js/irccd/js/timer_jsapi.cpp	Wed Dec 06 21:32:10 2017 +0100
@@ -18,9 +18,8 @@
 
 #include <boost/asio.hpp>
 
-#include <irccd/logger.hpp>
-
 #include <irccd/daemon/irccd.hpp>
+#include <irccd/daemon/logger.hpp>
 
 #include "irccd_jsapi.hpp"
 #include "js_plugin.hpp"
@@ -86,8 +85,10 @@
     duk_remove(ctx, -2);
 
     if (duk_pcall(ctx, 0)) {
-        log::warning() << "plugin: " << plugin->name() << " timer error:" << std::endl;
-        log::warning() << "  " << dukx_stack(ctx, -1).what() << std::endl;
+        dukx_get_irccd(ctx).log().warning() << "plugin: " << plugin->name()
+            << " timer error:" << std::endl;
+        dukx_get_irccd(ctx).log().warning() << "  "
+            << dukx_stack(ctx, -1).what() << std::endl;
     } else
         duk_pop(ctx);
 }
@@ -188,7 +189,7 @@
     duk_del_prop_string(ctx, -1, ptr->key().c_str());
     duk_pop(ctx);
 
-    log::debug("timer: destroyed");
+    dukx_get_irccd(ctx).log().debug("timer: destroyed");
 
     delete ptr;
 
--- a/libirccd-test/irccd/command_test.hpp	Wed Dec 06 14:12:57 2017 +0100
+++ b/libirccd-test/irccd/command_test.hpp	Wed Dec 06 21:32:10 2017 +0100
@@ -21,11 +21,10 @@
 
 #include <memory>
 
-#include <irccd/logger.hpp>
-
 #include <irccd/daemon/command_service.hpp>
 #include <irccd/daemon/ip_transport_server.hpp>
 #include <irccd/daemon/irccd.hpp>
+#include <irccd/daemon/logger.hpp>
 #include <irccd/daemon/transport_service.hpp>
 
 #include <irccd/ctl/ip_connection.hpp>
@@ -79,8 +78,6 @@
 {
     using boost::asio::ip::tcp;
 
-    log::set_logger(std::make_unique<log::silent_logger>());
-
     // Bind to a random port.
     tcp::endpoint ep(tcp::v4(), 0);
     tcp::acceptor acc(service_, ep);
@@ -91,6 +88,7 @@
 
     // Add the server and the command.
     add<Commands...>();
+    daemon_->set_log(std::make_unique<silent_logger>());
     daemon_->transports().add(std::make_unique<ip_transport_server>(std::move(acc)));
 
     timer_.expires_from_now(boost::posix_time::seconds(10));
--- a/libirccd-test/irccd/js_test.hpp	Wed Dec 06 14:12:57 2017 +0100
+++ b/libirccd-test/irccd/js_test.hpp	Wed Dec 06 21:32:10 2017 +0100
@@ -26,9 +26,8 @@
 
 #include <boost/asio.hpp>
 
-#include <irccd/logger.hpp>
-
 #include <irccd/daemon/irccd.hpp>
+#include <irccd/daemon/logger.hpp>
 
 #include <irccd/js/js_plugin.hpp>
 #include <irccd/js/irccd_jsapi.hpp>
@@ -71,7 +70,7 @@
         : plugin_(new js_plugin("test", plugin_path))
         , server_(new journal_server(service_, "test"))
     {
-        log::set_logger(std::make_unique<log::silent_logger>());
+        irccd_.set_log(std::make_unique<silent_logger>());
 
         // Irccd is mandatory at the moment.
         add<irccd_jsapi>();
--- a/libirccd-test/irccd/plugin_test.cpp	Wed Dec 06 14:12:57 2017 +0100
+++ b/libirccd-test/irccd/plugin_test.cpp	Wed Dec 06 21:32:10 2017 +0100
@@ -18,8 +18,7 @@
 
 #include <cassert>
 
-#include <irccd/logger.hpp>
-
+#include <irccd/daemon/logger.hpp>
 #include <irccd/daemon/plugin_service.hpp>
 #include <irccd/daemon/server_service.hpp>
 
@@ -43,12 +42,11 @@
 plugin_test::plugin_test(std::string name, std::string path)
     : server_(std::make_shared<journal_server>(service_, "test"))
 {
-    log::set_verbose(false);
-    log::set_logger(std::make_unique<log::silent_logger>());
-
     server_->set_nickname("irccd");
     plugin_ = std::make_unique<js_plugin>(std::move(name), std::move(path));
 
+    irccd_.log().set_verbose(false);
+    irccd_.set_log(std::make_unique<silent_logger>());
     irccd_.plugins().add(plugin_);
     irccd_.servers().add(server_);
 
--- a/libirccd/CMakeLists.txt	Wed Dec 06 14:12:57 2017 +0100
+++ b/libirccd/CMakeLists.txt	Wed Dec 06 21:32:10 2017 +0100
@@ -32,6 +32,7 @@
     ${libirccd_SOURCE_DIR}/irccd/daemon/irccd.hpp
     ${libirccd_SOURCE_DIR}/irccd/daemon/irc.hpp
     ${libirccd_SOURCE_DIR}/irccd/daemon/local_transport_server.hpp
+    ${libirccd_SOURCE_DIR}/irccd/daemon/logger.hpp
     ${libirccd_SOURCE_DIR}/irccd/daemon/plugin.hpp
     ${libirccd_SOURCE_DIR}/irccd/daemon/plugin_service.hpp
     ${libirccd_SOURCE_DIR}/irccd/daemon/rule.hpp
@@ -52,6 +53,7 @@
     ${libirccd_SOURCE_DIR}/irccd/daemon/dynlib_plugin.cpp
     ${libirccd_SOURCE_DIR}/irccd/daemon/irccd.cpp
     ${libirccd_SOURCE_DIR}/irccd/daemon/irc.cpp
+    ${libirccd_SOURCE_DIR}/irccd/daemon/logger.cpp
     ${libirccd_SOURCE_DIR}/irccd/daemon/plugin.cpp
     ${libirccd_SOURCE_DIR}/irccd/daemon/plugin_service.cpp
     ${libirccd_SOURCE_DIR}/irccd/daemon/rule.cpp
--- a/libirccd/irccd/daemon/irccd.cpp	Wed Dec 06 14:12:57 2017 +0100
+++ b/libirccd/irccd/daemon/irccd.cpp	Wed Dec 06 21:32:10 2017 +0100
@@ -16,12 +16,12 @@
  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  */
 
-#include <irccd/logger.hpp>
 #include <irccd/string_util.hpp>
 #include <irccd/system.hpp>
 
 #include "command_service.hpp"
 #include "irccd.hpp"
+#include "logger.hpp"
 #include "plugin_service.hpp"
 #include "rule_service.hpp"
 #include "server_service.hpp"
@@ -31,7 +31,7 @@
 
 namespace {
 
-class log_filter : public log::filter {
+class log_filter : public logger_filter {
 private:
     std::string info_;
     std::string warning_;
@@ -74,7 +74,9 @@
     }
 };
 
-void load_log_file(const ini::section& sc)
+} // !namespace
+
+void irccd::load_logs_file(const ini::section& sc)
 {
     /*
      * TODO: improve that with CMake options.
@@ -95,23 +97,21 @@
         errors = it->value();
 
     try {
-        log::set_logger(std::make_unique<log::file_logger>(std::move(normal), std::move(errors)));
+        logger_ = std::make_unique<file_logger>(std::move(normal), std::move(errors));
     } catch (const std::exception& ex) {
-        log::warning() << "logs: " << ex.what() << std::endl;
+        logger_->warning() << "logs: " << ex.what() << std::endl;
     }
 }
 
-void load_log_syslog()
+void irccd::load_logs_syslog()
 {
 #if defined(HAVE_SYSLOG)
-    log::set_logger(std::make_unique<log::syslog_logger>());
+    logger_ = std::make_unique<syslog_logger>();
 #else
-    log::warning() << "logs: syslog is not available on this platform" << std::endl;
+    logger_->warning() << "logs: syslog is not available on this platform" << std::endl;
 #endif // !HAVE_SYSLOG
 }
 
-} // !namespace
-
 void irccd::load_logs()
 {
     auto sc = config_.section("logs");
@@ -119,18 +119,18 @@
     if (sc.empty())
         return;
 
-    log::set_verbose(string_util::is_identifier(sc.get("verbose").value()));
+    logger_->set_verbose(string_util::is_identifier(sc.get("verbose").value()));
 
     auto type = sc.get("type").value();
 
     if (!type.empty()) {
         // Console is the default, no test case.
         if (type == "file")
-            load_log_file(sc);
+            load_logs_file(sc);
         else if (type == "syslog")
-            load_log_syslog();
+            load_logs_syslog();
         else if (type != "console")
-            log::warning() << "logs: invalid log type '" << type << std::endl;
+            logger_->warning() << "logs: invalid log type '" << type << std::endl;
     }
 }
 
@@ -141,7 +141,7 @@
     if (sc.empty())
         return;
 
-    log::set_filter(std::make_unique<log_filter>(
+    logger_->set_filter(std::make_unique<log_filter>(
         sc.get("info").value(),
         sc.get("warning").value(),
         sc.get("debug").value()
@@ -159,13 +159,13 @@
     std::ofstream out(path, std::ofstream::trunc);
 
     if (!out)
-        log::warning() << "irccd: could not open" << path << ": " << std::strerror(errno) << std::endl;
+        logger_->warning() << "irccd: could not open" << path << ": " << std::strerror(errno) << std::endl;
     else {
-        log::debug() << "irccd: pid written in " << path << std::endl;
+        logger_->debug() << "irccd: pid written in " << path << std::endl;
         out << getpid() << std::endl;
     }
 #else
-    log::warning() << "irccd: pidfile not supported on this platform" << std::endl;
+    logger_->warning() << "irccd: pidfile not supported on this platform" << std::endl;
 #endif
 }
 
@@ -179,12 +179,12 @@
 #if defined(HAVE_SETGID)
     try {
         sys::set_gid(gid);
-        log::info() << "irccd: setting gid to: " << gid << std::endl;
+        logger_->info() << "irccd: setting gid to: " << gid << std::endl;
     } catch (const std::exception& ex) {
-        log::warning() << "irccd: failed to set gid: " << ex.what() << std::endl;
+        logger_->warning() << "irccd: failed to set gid: " << ex.what() << std::endl;
     }
 #else
-    log::warning() << "irccd: gid option not supported" << std::endl;
+    logger_->warning() << "irccd: gid option not supported" << std::endl;
 #endif
 }
 
@@ -198,28 +198,36 @@
 #if defined(HAVE_SETUID)
     try {
         sys::set_uid(uid);
-        log::info() << "irccd: setting uid to: " << uid << std::endl;
+        logger_->info() << "irccd: setting uid to: " << uid << std::endl;
     } catch (const std::exception& ex) {
-        log::warning() << "irccd: failed to set uid: " << ex.what() << std::endl;
+        logger_->warning() << "irccd: failed to set uid: " << ex.what() << std::endl;
     }
 #else
-    log::warning() << "irccd: uid option not supported" << std::endl;
+    logger_->warning() << "irccd: uid option not supported" << std::endl;
 #endif
 }
 
 irccd::irccd(boost::asio::io_service& service, std::string config)
     : config_(std::move(config))
     , service_(service)
+    , logger_(std::make_unique<console_logger>())
     , command_service_(std::make_unique<command_service>())
     , server_service_(std::make_unique<server_service>(*this))
     , tpt_service_(std::make_unique<transport_service>(*this))
-    , rule_service_(std::make_unique<rule_service>())
+    , rule_service_(std::make_unique<rule_service>(*this))
     , plugin_service_(std::make_unique<plugin_service>(*this))
 {
 }
 
 irccd::~irccd() = default;
 
+void irccd::set_log(std::unique_ptr<logger> logger) noexcept
+{
+    assert(logger);
+
+    logger_ = std::move(logger);
+}
+
 void irccd::load() noexcept
 {
     /*
@@ -234,9 +242,9 @@
     load_formats();
 
     if (!loaded_)
-        log::info() << "irccd: loading configuration from " << config_.path() << std::endl;
+        logger_->info() << "irccd: loading configuration from " << config_.path() << std::endl;
     else
-        log::info() << "irccd: reloading configuration" << std::endl;
+        logger_->info() << "irccd: reloading configuration" << std::endl;
 
     // [general] section.
     if (!loaded_) {
--- a/libirccd/irccd/daemon/irccd.hpp	Wed Dec 06 14:12:57 2017 +0100
+++ b/libirccd/irccd/daemon/irccd.hpp	Wed Dec 06 21:32:10 2017 +0100
@@ -38,6 +38,7 @@
 namespace irccd {
 
 class command_service;
+class logger;
 class plugin_service;
 class rule_service;
 class server_service;
@@ -57,6 +58,9 @@
     // Tells if the configuration has already been called.
     bool loaded_{false};
 
+    // Custom logger.
+    std::unique_ptr<logger> logger_;
+
     // Services.
     std::shared_ptr<command_service> command_service_;
     std::shared_ptr<server_service> server_service_;
@@ -72,6 +76,8 @@
     irccd& operator=(irccd&&) = delete;
 
     // Load functions.
+    void load_logs_file(const ini::section&);
+    void load_logs_syslog();
     void load_logs();
     void load_formats();
     void load_pid();
@@ -133,6 +139,34 @@
     }
 
     /**
+     * Access the logger.
+     *
+     * \return the logger
+     */
+    inline const logger& log() const noexcept
+    {
+        return *logger_;
+    }
+
+    /**
+     * Overloaded function.
+     *
+     * \return the logger
+     */
+    inline logger& log() noexcept
+    {
+        return *logger_;
+    }
+
+    /**
+     * Set the logger.
+     *
+     * \pre logger != nullptr
+     * \param logger the new logger
+     */
+    void set_log(std::unique_ptr<logger> logger) noexcept;
+
+    /**
      * Access the command service.
      *
      * \return the service
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/libirccd/irccd/daemon/logger.cpp	Wed Dec 06 21:32:10 2017 +0100
@@ -0,0 +1,251 @@
+/*
+ * logger.cpp -- irccd logging
+ *
+ * Copyright (c) 2013-2017 David Demelier <markand@malikania.fr>
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <atomic>
+#include <cassert>
+#include <cerrno>
+#include <cstring>
+#include <fstream>
+#include <iostream>
+#include <stdexcept>
+#include <streambuf>
+
+#include "logger.hpp"
+#include "system.hpp"
+
+#if defined(HAVE_SYSLOG)
+#  include <syslog.h>
+#endif // !HAVE_SYSLOG
+
+namespace irccd {
+
+void logger::buffer::debug(std::string line)
+{
+    // Print only in debug mode, the buffer is flushed anyway.
+#if !defined(NDEBUG)
+    parent_.write_debug(parent_.filter_->pre_debug(std::move(line)));
+#else
+    (void)line;
+#endif
+}
+
+void logger::buffer::info(std::string line)
+{
+    // Print only if verbose, the buffer will be flushed anyway.
+    if (parent_.verbose_)
+        parent_.write_info(parent_.filter_->pre_info(std::move(line)));
+}
+
+void logger::buffer::warning(std::string line)
+{
+    parent_.write_warning(parent_.filter_->pre_warning(std::move(line)));
+}
+
+logger::buffer::buffer(logger& parent, level level) noexcept
+    : parent_(parent)
+    , level_(level)
+{
+    assert(level >= level::debug && level <= level::warning);
+}
+
+int logger::buffer::sync()
+{
+    std::string buffer = str();
+    std::string::size_type pos;
+
+    while ((pos = buffer.find("\n")) != std::string::npos) {
+        auto line = buffer.substr(0, pos);
+
+        // Remove this line.
+        buffer.erase(buffer.begin(), buffer.begin() + pos + 1);
+
+        switch (level_) {
+        case level::debug:
+            debug(std::move(line));
+            break;
+        case level::info:
+            info(std::move(line));
+            break;
+        case level::warning:
+            warning(std::move(line));
+            break;
+        default:
+            break;
+        }
+    }
+
+    str(buffer);
+
+    return 0;
+}
+
+/*
+ * console_logger
+ * ------------------------------------------------------------------
+ */
+
+void console_logger::write_info(const std::string& line)
+{
+    std::cout << line << std::endl;
+}
+
+void console_logger::write_warning(const std::string& line)
+{
+    std::cerr << line << std::endl;
+}
+
+void console_logger::write_debug(const std::string& line)
+{
+    std::cout << line << std::endl;
+}
+
+/*
+ * file_logger
+ * ------------------------------------------------------------------
+ */
+
+file_logger::file_logger(std::string normal, std::string errors)
+    : output_normal_(std::move(normal))
+    , output_error_(std::move(errors))
+{
+}
+
+void file_logger::write_info(const std::string& line)
+{
+    std::ofstream(output_normal_, std::ofstream::out | std::ofstream::app) << line << std::endl;
+}
+
+void file_logger::write_warning(const std::string& line)
+{
+    std::ofstream(output_error_, std::ofstream::out | std::ofstream::app) << line << std::endl;
+}
+
+void file_logger::write_debug(const std::string& line)
+{
+    std::ofstream(output_normal_, std::ofstream::out | std::ofstream::app) << line << std::endl;
+}
+
+/*
+ * silent_logger
+ * ------------------------------------------------------------------
+ */
+
+void silent_logger::write_info(const std::string&)
+{
+}
+
+void silent_logger::write_warning(const std::string&)
+{
+}
+
+void silent_logger::write_debug(const std::string&)
+{
+}
+
+/*
+ * syslog_logger
+ * ------------------------------------------------------------------
+ */
+
+#if defined(HAVE_SYSLOG)
+
+syslog_logger::syslog_logger()
+{
+    openlog("irccd", LOG_PID, LOG_DAEMON);
+}
+
+syslog_logger::~syslog_logger()
+{
+    closelog();
+}
+
+void syslog_logger::write_info(const std::string& line)
+{
+    syslog(LOG_INFO | LOG_USER, "%s", line.c_str());
+}
+
+void syslog_logger::write_warning(const std::string& line)
+{
+    syslog(LOG_WARNING | LOG_USER, "%s", line.c_str());
+}
+
+void syslog_logger::write_debug(const std::string& line)
+{
+    syslog(LOG_DEBUG | LOG_USER, "%s", line.c_str());
+}
+
+#endif // !HAVE_SYSLOG
+
+/*
+ * logger
+ * ------------------------------------------------------------------
+ */
+
+logger::logger()
+    : buffer_info_(*this, buffer::level::info)
+    , buffer_warning_(*this, buffer::level::warning)
+    , buffer_debug_(*this, buffer::level::debug)
+    , stream_info_(&buffer_info_)
+    , stream_warning_(&buffer_warning_)
+    , stream_debug_(&buffer_debug_)
+    , filter_(std::make_unique<logger_filter>())
+{
+}
+
+bool logger::is_verbose() const noexcept
+{
+    return verbose_;
+}
+
+void logger::set_verbose(bool mode) noexcept
+{
+    verbose_ = mode;
+}
+
+void logger::set_filter(std::unique_ptr<logger_filter> newfilter) noexcept
+{
+    assert(newfilter);
+
+    filter_ = std::move(newfilter);
+}
+
+std::ostream& logger::info(const std::string& message)
+{
+    if (!message.empty())
+        stream_info_ << message << std::endl;
+
+    return stream_info_;
+}
+
+std::ostream& logger::warning(const std::string& message)
+{
+    if (!message.empty())
+        stream_warning_ << message << std::endl;
+
+    return stream_warning_;
+}
+
+std::ostream& logger::debug(const std::string& message)
+{
+    if (!message.empty())
+        stream_debug_ << message << std::endl;
+
+    return stream_debug_;
+}
+
+} // !irccd
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/libirccd/irccd/daemon/logger.hpp	Wed Dec 06 21:32:10 2017 +0100
@@ -0,0 +1,346 @@
+/*
+ * logger.hpp -- irccd logging
+ *
+ * Copyright (c) 2013-2017 David Demelier <markand@malikania.fr>
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#ifndef IRCCD_DAEMON_LOGGER_HPP
+#define IRCCD_DAEMON_LOGGER_HPP
+
+/**
+ * \file logger.hpp
+ * \brief Logging facilities.
+ */
+
+#include <memory>
+#include <sstream>
+#include <utility>
+
+#include "sysconfig.hpp"
+
+namespace irccd {
+
+class logger_filter;
+
+/**
+ * \brief Interface to implement new logger mechanisms.
+ *
+ * Derive from this class and implement write_info, write_warning and
+ * write_debug functions.
+ *
+ * \see file_logger
+ * \see console_logger
+ * \see syslog_logger
+ * \see silent_logger
+ */
+class logger {
+private:
+    class buffer : public std::stringbuf {
+    public:
+        enum class level {
+            debug,
+            info,
+            warning
+        };
+
+    private:
+        logger& parent_;
+        level level_;
+
+        void debug(std::string line);
+        void info(std::string line);
+        void warning(std::string line);
+
+    public:
+        buffer(logger& parent, level level) noexcept;
+
+        virtual int sync() override;
+    };
+
+    // Buffers.
+    buffer buffer_info_;
+    buffer buffer_warning_;
+    buffer buffer_debug_;
+
+    // Stream outputs.
+    std::ostream stream_info_;
+    std::ostream stream_warning_;
+    std::ostream stream_debug_;
+
+    // User options.
+    bool verbose_{false};
+    std::unique_ptr<logger_filter> filter_;
+
+protected:
+    /**
+     * Write a debug message.
+     *
+     * This function is called only if NDEBUG is not defined.
+     *
+     * \param line the data
+     * \see log::debug
+     */
+    virtual void write_debug(const std::string& line) = 0;
+
+    /**
+     * Write a information message.
+     *
+     * The function is called only if verbose is true.
+     *
+     * \param line the data
+     * \see log::info
+     */
+    virtual void write_info(const std::string& line) = 0;
+
+    /**
+     * Write an error message.
+     *
+     * This function is always called.
+     *
+     * \param line the data
+     * \see log::warning
+     */
+    virtual void write_warning(const std::string& line) = 0;
+
+public:
+    /**
+     * Default constructor.
+     */
+    logger();
+
+    /**
+     * Virtual destructor defaulted.
+     */
+    virtual ~logger() = default;
+
+    /**
+     * Tells if logger is verbose.
+     *
+     * \return true if verbose
+     */
+    bool is_verbose() const noexcept;
+
+    /**
+     * Set the verbosity mode.
+     *
+     * \param mode the new mode
+     */
+    void set_verbose(bool mode) noexcept;
+
+    /**
+     * Set an optional filter.
+     *
+     * \pre filter must not be null
+     * \param filter the filter
+     */
+    void set_filter(std::unique_ptr<logger_filter> newfilter) noexcept;
+
+    /**
+     * 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(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(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(const std::string& message = "");
+};
+
+/**
+ * \brief Filter messages before printing them.
+ *
+ * Derive from this class and use log::setFilter.
+ */
+class logger_filter {
+public:
+    /**
+     * Virtual destructor defaulted.
+     */
+    virtual ~logger_filter() = default;
+
+    /**
+     * Update the debug message.
+     *
+     * \param input the message
+     * \return the updated message
+     */
+    virtual std::string pre_debug(std::string input) const
+    {
+        return input;
+    }
+
+    /**
+     * Update the information message.
+     *
+     * \param input the message
+     * \return the updated message
+     */
+    virtual std::string pre_info(std::string input) const
+    {
+        return input;
+    }
+
+    /**
+     * Update the warning message.
+     *
+     * \param input the message
+     * \return the updated message
+     */
+    virtual std::string pre_warning(std::string input) const
+    {
+        return input;
+    }
+};
+
+/**
+ * \brief Logger implementation for console output using std::cout and
+ *        std::cerr.
+ */
+class console_logger : public logger {
+protected:
+    /**
+     * \copydoc logger::debug
+     */
+    void write_debug(const std::string& line) override;
+
+    /**
+     * \copydoc logger::info
+     */
+    void write_info(const std::string& line) override;
+
+    /**
+     * \copydoc logger::warning
+     */
+    void write_warning(const std::string& line) override;
+};
+
+/**
+ * \brief Output to a files.
+ */
+class file_logger : public logger {
+private:
+    std::string output_normal_;
+    std::string output_error_;
+
+protected:
+    /**
+     * \copydoc logger::debug
+     */
+    void write_debug(const std::string& line) override;
+
+    /**
+     * \copydoc logger::info
+     */
+    void write_info(const std::string& line) override;
+
+    /**
+     * \copydoc logger::warning
+     */
+    void write_warning(const std::string& line) override;
+
+public:
+    /**
+     * Outputs to files.
+     *
+     * \param normal the path to the normal logs
+     * \param errors the path to the errors logs
+     */
+    file_logger(std::string normal, std::string errors);
+};
+
+/**
+ * \brief Use to disable logs.
+ *
+ * Useful for unit tests when some classes may emits log.
+ */
+class silent_logger : public logger {
+protected:
+    /**
+     * \copydoc logger::debug
+     */
+    void write_debug(const std::string& line) override;
+
+    /**
+     * \copydoc logger::info
+     */
+    void write_info(const std::string& line) override;
+
+    /**
+     * \copydoc logger::warning
+     */
+    void write_warning(const std::string& line) override;
+};
+
+#if defined(HAVE_SYSLOG)
+
+/**
+ * \brief Implements logger into syslog.
+ */
+class syslog_logger : public logger {
+protected:
+    /**
+     * \copydoc logger::debug
+     */
+    void write_debug(const std::string& line) override;
+
+    /**
+     * \copydoc logger::info
+     */
+    void write_info(const std::string& line) override;
+
+    /**
+     * \copydoc logger::warning
+     */
+    void write_warning(const std::string& line) override;
+
+public:
+    /**
+     * Open the syslog.
+     */
+    syslog_logger();
+
+    /**
+     * Close the syslog.
+     */
+    ~syslog_logger();
+};
+
+#endif // !HAVE_SYSLOG
+
+} // !irccd
+
+#endif // !IRCCD_DAEMON_LOGGER_HPP
--- a/libirccd/irccd/daemon/plugin_service.cpp	Wed Dec 06 14:12:57 2017 +0100
+++ b/libirccd/irccd/daemon/plugin_service.cpp	Wed Dec 06 21:32:10 2017 +0100
@@ -17,8 +17,8 @@
  */
 
 #include <irccd/string_util.hpp>
-#include <irccd/logger.hpp>
 
+#include "logger.hpp"
 #include "config.hpp"
 #include "irccd.hpp"
 #include "plugin_service.hpp"
@@ -53,7 +53,7 @@
         try {
             plugin->on_unload(irccd_);
         } catch (const std::exception& ex) {
-            log::warning() << "plugin: " << plugin->name() << ": " << ex.what() << std::endl;
+            irccd_.log().warning() << "plugin: " << plugin->name() << ": " << ex.what() << std::endl;
         }
     }
 }
@@ -223,7 +223,7 @@
             try {
                 load(name, option.value());
             } catch (const std::exception& ex) {
-                log::warning(ex.what());
+                irccd_.log().warning(ex.what());
             }
         }
     }
--- a/libirccd/irccd/daemon/rule_service.cpp	Wed Dec 06 14:12:57 2017 +0100
+++ b/libirccd/irccd/daemon/rule_service.cpp	Wed Dec 06 21:32:10 2017 +0100
@@ -18,10 +18,11 @@
 
 #include <stdexcept>
 
-#include <irccd/logger.hpp>
 #include <irccd/string_util.hpp>
 
 #include "config.hpp"
+#include "irccd.hpp"
+#include "logger.hpp"
 #include "rule_service.hpp"
 #include "string_util.hpp"
 
@@ -77,6 +78,11 @@
 
 } // !namespace
 
+rule_service::rule_service(irccd &irccd)
+    : irccd_(irccd)
+{
+}
+
 void rule_service::add(rule rule)
 {
     rules_.push_back(std::move(rule));
@@ -120,20 +126,20 @@
 {
     bool result = true;
 
-    log::debug(string_util::sprintf("rule: solving for server=%s, channel=%s, origin=%s, plugin=%s, event=%s",
+    irccd_.log().debug(string_util::sprintf("rule: solving for server=%s, channel=%s, origin=%s, plugin=%s, event=%s",
         server, channel, origin, plugin, event));
 
     int i = 0;
     for (const auto& rule : rules_) {
         auto action = rule.action() == rule::action_type::accept ? "accept" : "drop";
 
-        log::debug() << "  candidate "   << i++ << ":\n"
-                     << "    servers: "  << string_util::join(rule.servers()) << "\n"
-                     << "    channels: " << string_util::join(rule.channels()) << "\n"
-                     << "    origins: "  << string_util::join(rule.origins()) << "\n"
-                     << "    plugins: "  << string_util::join(rule.plugins()) << "\n"
-                     << "    events: "   << string_util::join(rule.events()) << "\n"
-                     << "    action: "   << action << std::endl;
+        irccd_.log().debug() << "  candidate "   << i++ << ":\n"
+            << "    servers: "  << string_util::join(rule.servers()) << "\n"
+            << "    channels: " << string_util::join(rule.channels()) << "\n"
+            << "    origins: "  << string_util::join(rule.origins()) << "\n"
+            << "    plugins: "  << string_util::join(rule.plugins()) << "\n"
+            << "    events: "   << string_util::join(rule.events()) << "\n"
+            << "    action: "   << action << std::endl;
 
         if (rule.match(server, channel, origin, plugin, event))
             result = rule.action() == rule::action_type::accept;
@@ -153,7 +159,7 @@
         try {
             rules_.push_back(load_rule(section));
         } catch (const std::exception& ex) {
-            log::warning() << "rule: " << ex.what() << std::endl;
+            irccd_.log().warning() << "rule: " << ex.what() << std::endl;
         }
     }
 }
--- a/libirccd/irccd/daemon/rule_service.hpp	Wed Dec 06 14:12:57 2017 +0100
+++ b/libirccd/irccd/daemon/rule_service.hpp	Wed Dec 06 21:32:10 2017 +0100
@@ -31,6 +31,7 @@
 namespace irccd {
 
 class config;
+class irccd;
 
 /**
  * \brief Store and solve rules.
@@ -38,10 +39,16 @@
  */
 class rule_service {
 private:
+    irccd& irccd_;
     std::vector<rule> rules_;
 
 public:
     /**
+     * Create the rule service.
+     */
+    rule_service(irccd& instance);
+
+    /**
      * Get the list of rules.
      *
      * \return the list of rules
--- a/libirccd/irccd/daemon/server.cpp	Wed Dec 06 14:12:57 2017 +0100
+++ b/libirccd/irccd/daemon/server.cpp	Wed Dec 06 21:32:10 2017 +0100
@@ -31,7 +31,6 @@
 #endif
 
 #include "json_util.hpp"
-#include "logger.hpp"
 #include "server.hpp"
 #include "string_util.hpp"
 #include "system.hpp"
@@ -138,10 +137,8 @@
     state_ = state_t::connected;
     on_connect({shared_from_this()});
 
-    for (const auto& channel : rchannels_) {
-        log::info() << "server " << name_ << ": auto joining " << channel.name << std::endl;
+    for (const auto& channel : rchannels_)
         join(channel.name, channel.password);
-    }
 }
 
 void server::dispatch_endofnames(const irc::message& msg)
@@ -202,6 +199,7 @@
         if (msg.arg(i).compare(0, 6, "PREFIX") == 0) {
             modes_ = isupport_extract_prefixes(msg.arg(i));
 
+#if 0
 #if !defined(NDEBUG)
             auto show = [this] (auto mode, auto title) {
                 auto it = modes_.find(mode);
@@ -217,6 +215,7 @@
             show(channel_mode::protection, "protection");
             show(channel_mode::voiced, "voiced");
 #endif // !NDEBUG
+#endif
 
             break;
         }
@@ -443,9 +442,6 @@
 {
     assert(state_ == state_t::identifying);
 
-    log::debug(string_util::sprintf("server %s: connected, identifying", name_));
-    log::debug(string_util::sprintf("server %s: verifying server", name_));
-
     if (!password_.empty())
         conn_->send(string_util::sprintf("PASS %s", password_));
 
@@ -468,20 +464,17 @@
 {
     if (code) {
         conn_ = nullptr;
+#if 0
         log::warning(string_util::sprintf("server %s: error while connecting", name_));
         log::warning(string_util::sprintf("server %s: %s", name_, code.message()));
+#endif
 
         // Wait before reconnecting.
         if (recotries_ != 0) {
             if (recotries_ > 0 && recocur_ >= recotries_) {
-                log::warning() << "server " << name_ << ": giving up" << std::endl;
-
                 state_ = state_t::disconnected;
                 on_die();
             } else {
-                log::warning() << "server " << name_ << ": retrying in " <<
-                    recodelay_ << " seconds" << std::endl;
-
                 state_ = state_t::waiting;
                 wait();
             }
@@ -518,6 +511,7 @@
 void server::connect() noexcept
 {
     assert(state_ == state_t::disconnected || state_ == state_t::waiting);
+
     /*
      * This is needed if irccd is started before DHCP or if DNS cache is
      * outdated.
--- a/libirccd/irccd/daemon/server_service.cpp	Wed Dec 06 14:12:57 2017 +0100
+++ b/libirccd/irccd/daemon/server_service.cpp	Wed Dec 06 21:32:10 2017 +0100
@@ -43,16 +43,17 @@
         auto allowed = daemon.rules().solve(server, target, origin, plugin->name(), eventname);
 
         if (!allowed) {
-            log::debug() << "rule: event skipped on match" << std::endl;
+            daemon.log().debug("rule: event skipped on match");
             continue;
         }
 
-        log::debug() << "rule: event allowed" << std::endl;
+        daemon.log().debug("rule: event allowed");
 
         try {
             exec_func(*plugin);
         } catch (const std::exception& ex) {
-            log::warning() << "plugin " << plugin->name() << ": error: " << ex.what() << std::endl;
+            daemon.log().warning() << "plugin " << plugin->name() << ": error: "
+                << ex.what() << std::endl;
         }
     }
 }
@@ -118,7 +119,7 @@
     if (value.empty())
         throw server_error(server_error::invalid_hostname, name);
 
-    return name;
+    return value.value();
 }
 
 std::string to_host(const nlohmann::json& object, const std::string& name)
@@ -242,7 +243,7 @@
 
 void server_service::handle_connect(const connect_event& ev)
 {
-    log::debug() << "server " << ev.server->name() << ": event onConnect" << std::endl;
+    irccd_.log().debug() << "server " << ev.server->name() << ": event onConnect" << std::endl;
 
     irccd_.transports().broadcast(nlohmann::json::object({
         { "event",      "onConnect"         },
@@ -261,10 +262,10 @@
 
 void server_service::handle_invite(const invite_event& ev)
 {
-    log::debug() << "server " << ev.server->name() << ": event onInvite:\n";
-    log::debug() << "  origin: " << ev.origin << "\n";
-    log::debug() << "  channel: " << ev.channel << "\n";
-    log::debug() << "  target: " << ev.nickname << std::endl;
+    irccd_.log().debug() << "server " << ev.server->name() << ": event onInvite:\n";
+    irccd_.log().debug() << "  origin: " << ev.origin << "\n";
+    irccd_.log().debug() << "  channel: " << ev.channel << "\n";
+    irccd_.log().debug() << "  target: " << ev.nickname << std::endl;
 
     irccd_.transports().broadcast(nlohmann::json::object({
         { "event",      "onInvite"          },
@@ -285,9 +286,9 @@
 
 void server_service::handle_join(const join_event& ev)
 {
-    log::debug() << "server " << ev.server->name() << ": event onJoin:\n";
-    log::debug() << "  origin: " << ev.origin << "\n";
-    log::debug() << "  channel: " << ev.channel << std::endl;
+    irccd_.log().debug() << "server " << ev.server->name() << ": event onJoin:\n";
+    irccd_.log().debug() << "  origin: " << ev.origin << "\n";
+    irccd_.log().debug() << "  channel: " << ev.channel << std::endl;
 
     irccd_.transports().broadcast(nlohmann::json::object({
         { "event",      "onJoin"            },
@@ -308,11 +309,11 @@
 
 void server_service::handle_kick(const kick_event& ev)
 {
-    log::debug() << "server " << ev.server->name() << ": event onKick:\n";
-    log::debug() << "  origin: " << ev.origin << "\n";
-    log::debug() << "  channel: " << ev.channel << "\n";
-    log::debug() << "  target: " << ev.target << "\n";
-    log::debug() << "  reason: " << ev.reason << std::endl;
+    irccd_.log().debug() << "server " << ev.server->name() << ": event onKick:\n";
+    irccd_.log().debug() << "  origin: " << ev.origin << "\n";
+    irccd_.log().debug() << "  channel: " << ev.channel << "\n";
+    irccd_.log().debug() << "  target: " << ev.target << "\n";
+    irccd_.log().debug() << "  reason: " << ev.reason << std::endl;
 
     irccd_.transports().broadcast(nlohmann::json::object({
         { "event",      "onKick"            },
@@ -335,10 +336,10 @@
 
 void server_service::handle_message(const message_event& ev)
 {
-    log::debug() << "server " << ev.server->name() << ": event onMessage:\n";
-    log::debug() << "  origin: " << ev.origin << "\n";
-    log::debug() << "  channel: " << ev.channel << "\n";
-    log::debug() << "  message: " << ev.message << std::endl;
+    irccd_.log().debug() << "server " << ev.server->name() << ": event onMessage:\n";
+    irccd_.log().debug() << "  origin: " << ev.origin << "\n";
+    irccd_.log().debug() << "  channel: " << ev.channel << "\n";
+    irccd_.log().debug() << "  message: " << ev.message << std::endl;
 
     irccd_.transports().broadcast(nlohmann::json::object({
         { "event",      "onMessage"         },
@@ -372,10 +373,10 @@
 
 void server_service::handle_me(const me_event& ev)
 {
-    log::debug() << "server " << ev.server->name() << ": event onMe:\n";
-    log::debug() << "  origin: " << ev.origin << "\n";
-    log::debug() << "  target: " << ev.channel << "\n";
-    log::debug() << "  message: " << ev.message << std::endl;
+    irccd_.log().debug() << "server " << ev.server->name() << ": event onMe:\n";
+    irccd_.log().debug() << "  origin: " << ev.origin << "\n";
+    irccd_.log().debug() << "  target: " << ev.channel << "\n";
+    irccd_.log().debug() << "  message: " << ev.message << std::endl;
 
     irccd_.transports().broadcast(nlohmann::json::object({
         { "event",      "onMe"              },
@@ -397,13 +398,13 @@
 
 void server_service::handle_mode(const mode_event& ev)
 {
-    log::debug() << "server " << ev.server->name() << ": event onMode\n";
-    log::debug() << "  origin: " << ev.origin << "\n";
-    log::debug() << "  channel: " << ev.channel << "\n";
-    log::debug() << "  mode: " << ev.mode << "\n";
-    log::debug() << "  limit: " << ev.limit << "\n";
-    log::debug() << "  user: " << ev.user << "\n";
-    log::debug() << "  mask: " << ev.mask << std::endl;
+    irccd_.log().debug() << "server " << ev.server->name() << ": event onMode\n";
+    irccd_.log().debug() << "  origin: " << ev.origin << "\n";
+    irccd_.log().debug() << "  channel: " << ev.channel << "\n";
+    irccd_.log().debug() << "  mode: " << ev.mode << "\n";
+    irccd_.log().debug() << "  limit: " << ev.limit << "\n";
+    irccd_.log().debug() << "  user: " << ev.user << "\n";
+    irccd_.log().debug() << "  mask: " << ev.mask << std::endl;
 
     irccd_.transports().broadcast(nlohmann::json::object({
         { "event",      "onMode"            },
@@ -428,9 +429,9 @@
 
 void server_service::handle_names(const names_event& ev)
 {
-    log::debug() << "server " << ev.server->name() << ": event onNames:\n";
-    log::debug() << "  channel: " << ev.channel << "\n";
-    log::debug() << "  names: " << string_util::join(ev.names.begin(), ev.names.end(), ", ") << std::endl;
+    irccd_.log().debug() << "server " << ev.server->name() << ": event onNames:\n";
+    irccd_.log().debug() << "  channel: " << ev.channel << "\n";
+    irccd_.log().debug() << "  names: " << string_util::join(ev.names.begin(), ev.names.end(), ", ") << std::endl;
 
     auto names = nlohmann::json::array();
 
@@ -456,9 +457,9 @@
 
 void server_service::handle_nick(const nick_event& ev)
 {
-    log::debug() << "server " << ev.server->name() << ": event onNick:\n";
-    log::debug() << "  origin: " << ev.origin << "\n";
-    log::debug() << "  nickname: " << ev.nickname << std::endl;
+    irccd_.log().debug() << "server " << ev.server->name() << ": event onNick:\n";
+    irccd_.log().debug() << "  origin: " << ev.origin << "\n";
+    irccd_.log().debug() << "  nickname: " << ev.nickname << std::endl;
 
     irccd_.transports().broadcast(nlohmann::json::object({
         { "event",      "onNick"            },
@@ -479,10 +480,10 @@
 
 void server_service::handle_notice(const notice_event& ev)
 {
-    log::debug() << "server " << ev.server->name() << ": event onNotice:\n";
-    log::debug() << "  origin: " << ev.origin << "\n";
-    log::debug() << "  channel: " << ev.channel << "\n";
-    log::debug() << "  message: " << ev.message << std::endl;
+    irccd_.log().debug() << "server " << ev.server->name() << ": event onNotice:\n";
+    irccd_.log().debug() << "  origin: " << ev.origin << "\n";
+    irccd_.log().debug() << "  channel: " << ev.channel << "\n";
+    irccd_.log().debug() << "  message: " << ev.message << std::endl;
 
     irccd_.transports().broadcast(nlohmann::json::object({
         { "event",      "onNotice"          },
@@ -504,10 +505,10 @@
 
 void server_service::handle_part(const part_event& ev)
 {
-    log::debug() << "server " << ev.server->name() << ": event onPart:\n";
-    log::debug() << "  origin: " << ev.origin << "\n";
-    log::debug() << "  channel: " << ev.channel << "\n";
-    log::debug() << "  reason: " << ev.reason << std::endl;
+    irccd_.log().debug() << "server " << ev.server->name() << ": event onPart:\n";
+    irccd_.log().debug() << "  origin: " << ev.origin << "\n";
+    irccd_.log().debug() << "  channel: " << ev.channel << "\n";
+    irccd_.log().debug() << "  reason: " << ev.reason << std::endl;
 
     irccd_.transports().broadcast(nlohmann::json::object({
         { "event",      "onPart"            },
@@ -529,10 +530,10 @@
 
 void server_service::handle_topic(const topic_event& ev)
 {
-    log::debug() << "server " << ev.server->name() << ": event onTopic:\n";
-    log::debug() << "  origin: " << ev.origin << "\n";
-    log::debug() << "  channel: " << ev.channel << "\n";
-    log::debug() << "  topic: " << ev.topic << std::endl;
+    irccd_.log().debug() << "server " << ev.server->name() << ": event onTopic:\n";
+    irccd_.log().debug() << "  origin: " << ev.origin << "\n";
+    irccd_.log().debug() << "  channel: " << ev.channel << "\n";
+    irccd_.log().debug() << "  topic: " << ev.topic << std::endl;
 
     irccd_.transports().broadcast(nlohmann::json::object({
         { "event",      "onTopic"           },
@@ -554,12 +555,12 @@
 
 void server_service::handle_whois(const whois_event& ev)
 {
-    log::debug() << "server " << ev.server->name() << ": event onWhois\n";
-    log::debug() << "  nickname: " << ev.whois.nick << "\n";
-    log::debug() << "  username: " << ev.whois.user << "\n";
-    log::debug() << "  host: " << ev.whois.host << "\n";
-    log::debug() << "  realname: " << ev.whois.realname << "\n";
-    log::debug() << "  channels: " << string_util::join(ev.whois.channels, ", ") << std::endl;
+    irccd_.log().debug() << "server " << ev.server->name() << ": event onWhois\n";
+    irccd_.log().debug() << "  nickname: " << ev.whois.nick << "\n";
+    irccd_.log().debug() << "  username: " << ev.whois.user << "\n";
+    irccd_.log().debug() << "  host: " << ev.whois.host << "\n";
+    irccd_.log().debug() << "  realname: " << ev.whois.realname << "\n";
+    irccd_.log().debug() << "  channels: " << string_util::join(ev.whois.channels, ", ") << std::endl;
 
     irccd_.transports().broadcast(nlohmann::json::object({
         { "event",      "onWhois"           },
@@ -652,7 +653,7 @@
         auto server = ptr.lock();
 
         if (server) {
-            log::info(string_util::sprintf("server %s: removed", server->name()));
+            irccd_.log().info(string_util::sprintf("server %s: removed", server->name()));
             servers_.erase(std::find(servers_.begin(), servers_.end(), server));
         }
     });
@@ -712,7 +713,7 @@
         try {
             add(load_server(irccd_.service(), cfg, section));
         } catch (const std::exception& ex) {
-            log::warning() << "server " << section.get("name").value() << ": "
+            irccd_.log().warning() << "server " << section.get("name").value() << ": "
                 << ex.what() << std::endl;
         }
     }
--- a/libirccd/irccd/daemon/transport_service.cpp	Wed Dec 06 14:12:57 2017 +0100
+++ b/libirccd/irccd/daemon/transport_service.cpp	Wed Dec 06 21:32:10 2017 +0100
@@ -112,10 +112,8 @@
 
     boost::asio::ip::tcp::acceptor acceptor(service, endpoint, true);
 
-    if (pkey.empty()) {
-        log::info() << "transport: listening on " << port << std::endl;
+    if (pkey.empty())
         return std::make_unique<ip_transport_server>(std::move(acceptor));
-    }
 
 #if defined(HAVE_SSL)
     boost::asio::ssl::context ctx(boost::asio::ssl::context::sslv23);
@@ -123,8 +121,6 @@
     ctx.use_private_key_file(pkey, boost::asio::ssl::context::pem);
     ctx.use_certificate_file(cert, boost::asio::ssl::context::pem);
 
-    log::info() << "transport: listening on " << port << " using SSL" << std::endl;
-
     return std::make_unique<tls_transport_server>(std::move(acceptor), std::move(ctx));
 #else
     throw std::invalid_argument("SSL disabled");
@@ -149,8 +145,6 @@
     stream_protocol::endpoint endpoint(it->value());
     stream_protocol::acceptor acceptor(service, std::move(endpoint));
 
-    log::info() << "transport: listening on " << it->value() << std::endl;
-
     return std::make_unique<local_transport_server>(std::move(acceptor));
 #else
     (void)sc;
@@ -205,8 +199,8 @@
         } catch (const boost::system::system_error& ex) {
             tc->error(ex.code(), cmd->name());
         } catch (const std::exception& ex) {
-            log::warning() << "transport: unknown error not reported" << std::endl;
-            log::warning() << "transport: " << ex.what() << std::endl;
+            irccd_.log().warning() << "transport: unknown error not reported" << std::endl;
+            irccd_.log().warning() << "transport: " << ex.what() << std::endl;
         }
     }
 }
@@ -216,7 +210,7 @@
     tc->recv([this, tc] (auto code, auto json) {
         switch (code.value()) {
         case boost::system::errc::network_down:
-            log::warning("transport: client disconnected");
+            irccd_.log().warning("transport: client disconnected");
             break;
             case boost::system::errc::invalid_argument:
             tc->error(irccd_error::invalid_message);
@@ -236,12 +230,12 @@
 {
     ts.accept([this, &ts] (auto code, auto client) {
         if (code)
-            log::warning() << "transport: new client error: " << code.message() << std::endl;
+            irccd_.log().warning() << "transport: new client error: " << code.message() << std::endl;
         else {
             do_accept(ts);
             do_recv(std::move(client));
 
-            log::info() << "transport: new client connected" << std::endl;
+            irccd_.log().info() << "transport: new client connected" << std::endl;
         }
     });
 }
@@ -279,7 +273,7 @@
         try {
             add(load_transport(irccd_.service(), section));
         } catch (const std::exception& ex) {
-            log::warning() << "transport: " << ex.what() << std::endl;
+            irccd_.log().warning() << "transport: " << ex.what() << std::endl;
         }
     }
 }
--- a/tests/src/logger-jsapi/main.cpp	Wed Dec 06 14:12:57 2017 +0100
+++ b/tests/src/logger-jsapi/main.cpp	Wed Dec 06 21:32:10 2017 +0100
@@ -19,7 +19,7 @@
 #define BOOST_TEST_MODULE "Logger Javascript API"
 #include <boost/test/unit_test.hpp>
 
-#include <irccd/logger.hpp>
+#include <irccd/daemon/logger.hpp>
 
 #include <irccd/js/logger_jsapi.hpp>
 #include <irccd/js/plugin_jsapi.hpp>
@@ -34,7 +34,7 @@
     std::string line_warning;
     std::string line_debug;
 
-    class my_logger : public log::logger {
+    class my_logger : public logger {
     private:
         logger_test& test_;
 
@@ -44,17 +44,17 @@
         {
         }
 
-        void info(const std::string& line) override
+        void write_info(const std::string& line) override
         {
             test_.line_info = line;
         }
 
-        void warning(const std::string& line) override
+        void write_warning(const std::string& line) override
         {
             test_.line_warning = line;
         }
 
-        void debug(const std::string& line) override
+        void write_debug(const std::string& line) override
         {
             test_.line_debug = line;
         }
@@ -62,8 +62,8 @@
 
     logger_test()
     {
-        log::set_verbose(true);
-        log::set_logger(std::make_unique<my_logger>(*this));
+        irccd_.set_log(std::make_unique<my_logger>(*this));
+        irccd_.log().set_verbose(true);
     }
 };
 
--- a/tests/src/logger/main.cpp	Wed Dec 06 14:12:57 2017 +0100
+++ b/tests/src/logger/main.cpp	Wed Dec 06 21:32:10 2017 +0100
@@ -21,65 +21,58 @@
 #define BOOST_TEST_MODULE "Logger"
 #include <boost/test/unit_test.hpp>
 
-#include <irccd/logger.hpp>
+#include <irccd/daemon/logger.hpp>
 
 namespace irccd {
 
-class logger_test {
+class my_logger : public logger {
 public:
     std::string line_debug;
     std::string line_info;
     std::string line_warning;
 
-    class my_logger : public log::logger {
-    private:
-        logger_test& test_;
-
-    public:
-        inline my_logger(logger_test& test) noexcept
-            : test_(test)
-        {
-        }
+    void write_debug(const std::string& line) override
+    {
+        line_debug = line;
+    }
 
-        void debug(const std::string& line) override
-        {
-            test_.line_debug = line;
-        }
+    void write_info(const std::string& line) override
+    {
+        line_info = line;
+    }
 
-        void info(const std::string& line) override
-        {
-            test_.line_info = line;
-        }
+    void write_warning(const std::string& line) override
+    {
+        line_warning = line;
+    }
+};
 
-        void warning(const std::string& line) override
-        {
-            test_.line_warning = line;
-        }
-    };
+class my_filter : public logger_filter {
+public:
+    std::string pre_debug(std::string input) const override
+    {
+        return std::reverse(input.begin(), input.end()), input;
+    }
 
-    class my_filter : public log::filter {
-    public:
-        std::string pre_debug(std::string input) const override
-        {
-            return std::reverse(input.begin(), input.end()), input;
-        }
+    std::string pre_info(std::string input) const override
+    {
+        return std::reverse(input.begin(), input.end()), input;
+    }
 
-        std::string pre_info(std::string input) const override
-        {
-            return std::reverse(input.begin(), input.end()), input;
-        }
+    std::string pre_warning(std::string input) const override
+    {
+        return std::reverse(input.begin(), input.end()), input;
+    }
+};
 
-        std::string pre_warning(std::string input) const override
-        {
-            return std::reverse(input.begin(), input.end()), input;
-        }
-    };
+class logger_test {
+public:
+    my_logger log_;
 
     logger_test()
     {
-        log::set_logger(std::make_unique<my_logger>(*this));
-        log::set_filter(std::make_unique<my_filter>());
-        log::set_verbose(true);
+        log_.set_filter(std::make_unique<my_filter>());
+        log_.set_verbose(true);
     }
 };
 
@@ -89,33 +82,33 @@
 
 BOOST_AUTO_TEST_CASE(debug)
 {
-    log::debug("debug");
+    log_.debug("debug");
 
-    BOOST_REQUIRE_EQUAL("gubed", line_debug);
+    BOOST_REQUIRE_EQUAL("gubed", log_.line_debug);
 }
 
 #endif
 
 BOOST_AUTO_TEST_CASE(info)
 {
-    log::info("info");
+    log_.info("info");
 
-    BOOST_REQUIRE_EQUAL("ofni", line_info);
+    BOOST_REQUIRE_EQUAL("ofni", log_.line_info);
 }
 
 BOOST_AUTO_TEST_CASE(info_quiet)
 {
-    log::set_verbose(false);
-    log::info("info");
+    log_.set_verbose(false);
+    log_.info("info");
 
-    BOOST_REQUIRE(line_info.empty());
+    BOOST_REQUIRE(log_.line_info.empty());
 }
 
 BOOST_AUTO_TEST_CASE(warning)
 {
-    log::warning("warning");
+    log_.warning("warning");
 
-    BOOST_REQUIRE_EQUAL("gninraw", line_warning);
+    BOOST_REQUIRE_EQUAL("gninraw", log_.line_warning);
 }
 
 BOOST_AUTO_TEST_SUITE_END()
--- a/tests/src/plugin-logger/main.cpp	Wed Dec 06 14:12:57 2017 +0100
+++ b/tests/src/plugin-logger/main.cpp	Wed Dec 06 21:32:10 2017 +0100
@@ -22,8 +22,6 @@
 #define BOOST_TEST_MODULE "Logger plugin"
 #include <boost/test/unit_test.hpp>
 
-#include <irccd/logger.hpp>
-
 #include <irccd/daemon/irccd.hpp>
 #include <irccd/daemon/server.hpp>
 
--- a/tests/src/plugin-plugin/main.cpp	Wed Dec 06 14:12:57 2017 +0100
+++ b/tests/src/plugin-plugin/main.cpp	Wed Dec 06 21:32:10 2017 +0100
@@ -19,7 +19,6 @@
 #define BOOST_TEST_MODULE "Plugin plugin"
 #include <boost/test/unit_test.hpp>
 
-#include <irccd/logger.hpp>
 #include <irccd/string_util.hpp>
 
 #include <irccd/daemon/irccd.hpp>
--- a/tests/src/rules/main.cpp	Wed Dec 06 14:12:57 2017 +0100
+++ b/tests/src/rules/main.cpp	Wed Dec 06 21:32:10 2017 +0100
@@ -19,7 +19,8 @@
 #define BOOST_TEST_MODULE "Rules"
 #include <boost/test/unit_test.hpp>
 
-#include <irccd/logger.hpp>
+#include <irccd/daemon/irccd.hpp>
+#include <irccd/daemon/logger.hpp>
 #include <irccd/daemon/rule_service.hpp>
 
 namespace irccd {
@@ -66,11 +67,13 @@
  */
 class rules_test {
 protected:
-    rule_service rules_;
+    boost::asio::io_service service_;
+    irccd daemon_{service_};
+    rule_service rules_{daemon_};
 
     rules_test()
     {
-        log::set_logger(std::make_unique<log::silent_logger>());
+        daemon_.set_log(std::make_unique<silent_logger>());
 
         // #1
         {