一个简单实用的 C++ logger

一个简单实用的 C++ logger

  • Log4cxx like, more powerful and slight stream logger
  • Thread-safe and with max log size limit(deque old logs when full)
  • Full ans esay set...()interfaces
  • Logging hook callback and tag(name) filter support
  • Dynamic log level
  • Friendly and source tarceable logger helper support

Full code, example and release package see:

Part code:

constexpr char const* kPrimaryDefaultLogFile = "logger.log";
constexpr uint32_t kMinLogSize = 8192;
/// 256 MB / 128 MB
constexpr uint32_t kDefaultLogSize = sizeof(long) * 32 * 1024 * 1024;
struct Logger {
    /// Output type
    enum Output: uint32_t {
        CoutOrCerr = 0x1,
        File       = 0x2,
        Both       = 0x3,
    };
    /// If 0 => when get logger not change current or default
    using Outputs = Flags<Output>;
    /// Callback when set when append
    using AppendCallback = std::function<void(
        std::string const& name,
        LogLevel const& logLevel,
        std::string const& msg)>;
    /// Dtor to auto finish logger
    virtual ~Logger() noexcept;
    // Global configs
    /// Set default logger, set only when path not empty
    static inline void setDefaultLogger(std::string const& path) noexcept;
    static inline std::string getDefaultLogger() noexcept;
    /**
     * Get a Logger instance
     * @param spinOnceLogLevel If not Unchange, will be use once
     * @param path Logger filename, if empty then will get a default Logger
     * @param outputs output config, used when create logger, if 0 then use
     * default or current exists
     */
    static Logger& getLogger(
        LogLevel const& spinOnceLogLevel = LogLevel::Unchange,
        std::string const& path = "",
        Outputs const& outputs = Outputs{}) noexcept;
    static inline Logger& getFatal(
        std::string const& path = "",
        Outputs const& outputs = Outputs{}) noexcept;
    static inline Logger& getError(
        std::string const& path = "",
        Outputs const& outputs = Outputs{}) noexcept;
    static inline Logger& getWarning(
        std::string const& path = "",
        Outputs const& outputs = Outputs{}) noexcept;
    static inline Logger& getNote(
        std::string const& path = "",
        Outputs const& outputs = Outputs{}) noexcept;
    static inline Logger& getInfo(
        std::string const& path = "",
        Outputs const& outputs = Outputs{}) noexcept;
    static inline Logger& getTrace(
        std::string const& path = "",
        Outputs const& outputs = Outputs{}) noexcept;
    static inline Logger& getDebug(
        std::string const& path = "",
        Outputs const& outputs = Outputs{}) noexcept;
    static inline Logger& getDetail(
        std::string const& path = "",
        Outputs const& outputs = Outputs{}) noexcept;
    /// Release a logger
    static void releaseLogger(std::string const& file) noexcept;
    // Instance config
    void setLogLevel(LogLevel const& logLevel) noexcept;
    /// Toggle log level
    LogLevel toggleLogLevel() noexcept;
    /**
     * Set max log size
     * - If < 0 keep current max
     * - If < kMinLogSize use kMinLogSize
     */
    void setMaxSize(int32_t const maxSize) noexcept;
    inline void setOutputs(Outputs const& o) noexcept;
    inline void setAppendCallback(AppendCallback const& ac) noexcept;
    inline void enableIdx(bool const enable) noexcept;
    inline void enableTid(bool const enable) noexcept;
    /// @note copy
    std::set<std::string> getAcNameFilters() const noexcept;
    bool hasAcNameFilter(std::string const& acNameFilter) const noexcept;
    bool addAcNameFilter(std::string const& acNameFilter) noexcept;
    bool removeAcNameFilter(std::string const& acNameFilter) noexcept;
    void clearAcNameFilters() noexcept;
    /// Limit log size
    void shrinkToFit() noexcept;
    /// Check if log instance valid
    inline operator bool() const noexcept;
    /// @note Only check log level
    inline bool isLogable(LogLevel const& ll) const noexcept;
    inline LogLevel getLogLevel() const noexcept;
    /**
     * Reset logger file
     * @note
     * - When param valid => will always open or reopen
     * - When param invalid => will use default or not-reopen
     */
    int64_t reset(bool const trunc = false) noexcept;
    /// Append name + file + line + msg
    int append(
        char const* const name,
        char const* const file,
        int const line,
        std::string const& msg,
        LogLevel const& logLevel) noexcept;
    /// Append a string msg
    int append(std::string const& msg, LogLevel const& logLevel) noexcept;
    // Logging methods
    // f
    template<typename T = std::string>
    typename std::enable_if<std::is_same<std::string,
        typename std::decay<T>::type>::value, Logger>::type& f(T const& msg)
        noexcept;
    template<typename T = std::string>
    typename std::enable_if<std::is_same<std::string,
        typename std::decay<T>::type>::value, Logger>::type& f(
        std::string const& name, T const& msg) noexcept;
    template<typename T>
    typename std::enable_if<!std::is_same<std::string,
        typename std::decay<T>::type>::value, Logger>::type& f(T const& msg)
        noexcept;
    template<typename T>
    typename std::enable_if<!std::is_same<std::string,
        typename std::decay<T>::type>::value, Logger>::type& f(
        std::string const& name, T const& msg) noexcept;
    template<uint32_t sz> Logger& f(const char(&msg)[sz]) noexcept;
    template<uint32_t sz> Logger& f(
        std::string const& name, const char(&msg)[sz]) noexcept;
    // e
    template<typename T = std::string>
    typename std::enable_if<std::is_same<std::string,
        typename std::decay<T>::type>::value, Logger>::type& e(T const& msg)
        noexcept;
    template<typename T = std::string>
    typename std::enable_if<std::is_same<std::string,
        typename std::decay<T>::type>::value, Logger>::type& e(
        std::string const& name, T const& msg) noexcept;
    template<typename T>
    typename std::enable_if<!std::is_same<std::string,
        typename std::decay<T>::type>::value, Logger>::type& e(T const& msg)
        noexcept;
    template<typename T>
    typename std::enable_if<!std::is_same<std::string,
        typename std::decay<T>::type>::value, Logger>::type& e(
        std::string const& name, T const& msg) noexcept;
    template<uint32_t sz> Logger& e(char const(&msg)[sz]) noexcept;
    template<uint32_t sz> Logger& e(
        std::string const& name, char const(&msg)[sz]) noexcept;
    // w
    template<typename T = std::string>
    typename std::enable_if<std::is_same<std::string,
        typename std::decay<T>::type>::value, Logger>::type& w(T const& msg)
        noexcept;
    template<typename T = std::string>
    typename std::enable_if<std::is_same<std::string,
        typename std::decay<T>::type>::value, Logger>::type& w(
        std::string const& name, T const& msg) noexcept;
    template<typename T>
    typename std::enable_if<!std::is_same<std::string,
        typename std::decay<T>::type>::value, Logger>::type& w(T const& msg)
        noexcept;
    template<typename T>
    typename std::enable_if<!std::is_same<std::string,
        typename std::decay<T>::type>::value, Logger>::type& w(
        std::string const& name, T const& msg) noexcept;
    template<uint32_t sz> Logger& w(char const(&msg)[sz]) noexcept;
    template<uint32_t sz> Logger& w(
        std::string const& name, char const(&msg)[sz]) noexcept;
    // n
    template<typename T = std::string>
    typename std::enable_if<std::is_same<std::string,
        typename std::decay<T>::type>::value, Logger>::type& n(T const& msg)
        noexcept;
    template<typename T = std::string>
    typename std::enable_if<std::is_same<std::string,
        typename std::decay<T>::type>::value, Logger>::type& n(
        std::string const& name, T const& msg) noexcept;
    template<typename T>
    typename std::enable_if<!std::is_same<std::string,
        typename std::decay<T>::type>::value, Logger>::type& n(T const& msg)
        noexcept;
    template<typename T>
    typename std::enable_if<!std::is_same<std::string,
        typename std::decay<T>::type>::value, Logger>::type& n(
        std::string const& name, T const& msg) noexcept;
    template<uint32_t sz> Logger& n(char const(&msg)[sz]) noexcept;
    template<uint32_t sz> Logger& n(
        std::string const& name, char const(&msg)[sz]) noexcept;
    // i
    template<typename T = std::string>
    typename std::enable_if<std::is_same<std::string,
        typename std::decay<T>::type>::value, Logger>::type& i(T const& msg)
        noexcept;
    template<typename T = std::string>
    typename std::enable_if<std::is_same<std::string,
        typename std::decay<T>::type>::value, Logger>::type& i(
        std::string const& name, T const& msg) noexcept;
    template<typename T>
    typename std::enable_if<!std::is_same<std::string,
        typename std::decay<T>::type>::value, Logger>::type& i(T const& msg)
        noexcept;
    template<typename T>
    typename std::enable_if<!std::is_same<std::string,
        typename std::decay<T>::type>::value, Logger>::type& i(
        std::string const& name, T const& msg) noexcept;
    template<uint32_t sz> Logger& i(char const(&msg)[sz]) noexcept;
    template<uint32_t sz> Logger& i(
        std::string const& name, char const(&msg)[sz]) noexcept;
    // d
    template<typename T = std::string>
    typename std::enable_if<std::is_same<std::string,
        typename std::decay<T>::type>::value, Logger>::type& d(T const& msg)
        noexcept;
    template<typename T = std::string>
    typename std::enable_if<std::is_same<std::string,
        typename std::decay<T>::type>::value, Logger>::type& d(
        std::string const& name, T const& msg) noexcept;
    template<typename T>
    typename std::enable_if<!std::is_same<std::string,
        typename std::decay<T>::type>::value, Logger>::type& d(T const& msg)
        noexcept;
    template<typename T>
    typename std::enable_if<!std::is_same<std::string,
        typename std::decay<T>::type>::value, Logger>::type& d(
        std::string const& name, T const& msg) noexcept;
    template<uint32_t sz> Logger& d(char const(&msg)[sz]) noexcept;
    template<uint32_t sz> Logger& d(
        std::string const& name, char const(&msg)[sz]) noexcept;
    // <<
    template<typename T = std::string>
    typename std::enable_if<std::is_same<std::string,
        typename std::decay<T>::type>::value, Logger>::type& operator<<(
        T const& msg) noexcept;
    template<typename T>
    typename std::enable_if<!std::is_same<std::string,
        typename std::decay<T>::type>::value, Logger>::type& operator<<(
        T const& msg) noexcept;
    template<uint32_t sz>
    Logger& operator<<(char const(&msg)[sz]) noexcept;
    // ,
    template<typename T = std::string>
    typename std::enable_if<std::is_same<std::string,
        typename std::decay<T>::type>::value, Logger>::type& operator,(
        T const& msg) noexcept;
    template<typename T>
    typename std::enable_if<!std::is_same<std::string,
        typename std::decay<T>::type>::value, Logger>::type& operator,(
        T const& msg) noexcept;
    template<uint32_t sz>
    Logger& operator,(char const(&msg)[sz]) noexcept;
    /// Finish log
    void finish() noexcept;
protected:
    // Some global options
    static std::string defaultLogFile;
    static Logger& hasLogger(std::string const& file) noexcept;
    /**
     * Create a logger
     * @param path if path empty only for internal emptyLogger
     * @param outputs if 0 then use default
     * @param maxSize if < 0 use kDefaultLogSize, else min kMinLogSize
     * @param trunc true to trunc file, used only when path not nil and
     * Output::File set
     */
    Logger(
        std::string const& path,
        Outputs const& outputs = Outputs{},
        int32_t const maxSize = -1,
        bool const trunc = false) noexcept;
    Logger(Logger const&) = delete;
    Logger& operator=(Logger const&) = delete;
    void tryDoAcCb(
        std::string const& name,
        LogLevel const& logLevel,
        std::string const &msg) const noexcept;
    // Logger instances and related
    static Logger emptyLogger;
    static boost::shared_mutex instancesRwlock;
    static std::map<std::string, SharedPtr<Logger> > instances;
    // Instance properties
    mutable std::mutex writemutex;
    LogLevel logLevel { LogLevel::Note };
    LogLevel spinOnceLogLevel{ LogLevel::Unchange };
    FILE* log{ nullptr };
    std::string const path;
    Outputs outputs{ Output::CoutOrCerr };
    uint32_t maxSize{ kDefaultLogSize };
    bool hasIdx{ true };
    bool hasTid{ false };
    AppendCallback appendCallback{ nullptr };
    mutable boost::shared_mutex acNameFiltersRwlock;
    /// @note empty name to filter nil and empty
    std::set<std::string> acNameFilters;
};
extern std::string LogRealTime() noexcept;
inline void Logger::setDefaultLogger(std::string const& path) noexcept
{
    if (!path.empty()) {
        Logger::defaultLogFile = path;
    }
}
inline std::string Logger::getDefaultLogger() noexcept
{
    return Logger::defaultLogFile;
}
inline Logger& Logger::getFatal(
    std::string const& path, Outputs const& outputs) noexcept
{
    return Logger::getLogger(LogLevel::Fata, path, outputs);
}
inline Logger& Logger::getError(
    std::string const& path, Outputs const& outputs) noexcept
{
    return Logger::getLogger(LogLevel::Erro, path, outputs);
}
inline Logger& Logger::getWarning(
    std::string const& path, Outputs const& outputs) noexcept
{
    return Logger::getLogger(LogLevel::Warn, path, outputs);
}
inline Logger& Logger::getNote(
    std::string const& path, Outputs const& outputs) noexcept
{
    return Logger::getLogger(LogLevel::Note, path, outputs);
}
inline Logger& Logger::getInfo(
    std::string const& path, Outputs const& outputs) noexcept
{
    return Logger::getLogger(LogLevel::Info, path, outputs);
}
inline Logger& Logger::getTrace(
    std::string const& path, Outputs const& outputs) noexcept
{
    return Logger::getLogger(LogLevel::Trac, path, outputs);
}
inline Logger& Logger::getDebug(
    std::string const& path, Outputs const& outputs) noexcept
{
    return Logger::getLogger(LogLevel::Debu, path, outputs);
}
inline Logger& Logger::getDetail(
    std::string const& path, Outputs const& outputs) noexcept
{
    return Logger::getLogger(LogLevel::Deta, path, outputs);
}
inline Logger::operator bool() const noexcept
{
    return !this->path.empty();
}
inline bool Logger::isLogable(LogLevel const& ll) const noexcept
{
    return this->logLevel >= ll;
}
inline LogLevel Logger::getLogLevel() const noexcept
{
    return this->logLevel;
}
inline void Logger::setOutputs(Outputs const& o) noexcept
{
    this->outputs = o;
}
inline void Logger::setAppendCallback(AppendCallback const& ac) noexcept
{
    this->appendCallback = ac;
}
inline void Logger::enableIdx(bool const enable) noexcept
{
    this->hasIdx = enable;
}
inline void Logger::enableTid(bool const enable) noexcept
{
    this->hasTid = enable;
}
// f
template<typename T>
typename std::enable_if<std::is_same<std::string,
    typename std::decay<T>::type>::value, Logger>::type& Logger::f(
    T const& msg) noexcept
{
    this->append(nullptr, nullptr, -1, msg, LogLevel::Fata);
    return *this;
}
template<typename T>
typename std::enable_if<std::is_same<std::string,
    typename std::decay<T>::type>::value, Logger>::type& Logger::f(
    std::string const& name, T const& msg) noexcept
{
    this->append(name.c_str(), nullptr, -1, msg, LogLevel::Fata);
    return *this;
}
template<typename T>
typename std::enable_if<!std::is_same<std::string,
    typename std::decay<T>::type>::value, Logger>::type& Logger::f(
    T const& msg) noexcept
{
    std::stringstream ss;
    ss << msg;
    this->append(nullptr, nullptr, -1, ss.str(), LogLevel::Fata);
    return *this;
}
template<typename T>
typename std::enable_if<!std::is_same<std::string,
    typename std::decay<T>::type>::value, Logger>::type& Logger::f(
    std::string const& name, T const& msg) noexcept
{
    std::stringstream ss;
    ss << msg;
    this->append(name.c_str(), nullptr, -1, ss.str(), LogLevel::Fata);
    return *this;
}
template<uint32_t sz> Logger& Logger::f(char const(&msg)[sz]) noexcept
{
    this->append(nullptr, nullptr, -1, msg, LogLevel::Fata);
    return *this;
}
template<uint32_t sz> Logger& Logger::f(
    std::string const& name, char const(&msg)[sz]) noexcept
{
    this->append(name, nullptr, -1, msg, LogLevel::Fata);
    return *this;
}
// e
template<typename T>
typename std::enable_if<std::is_same<std::string,
    typename std::decay<T>::type>::value, Logger>::type& Logger::e(
    T const& msg) noexcept
{
    this->append(nullptr, nullptr, -1, msg, LogLevel::Erro);
    return *this;
}
template<typename T>
typename std::enable_if<std::is_same<std::string,
    typename std::decay<T>::type>::value, Logger>::type& Logger::e(
    std::string const& name, T const& msg) noexcept
{
    this->append(name.c_str(), nullptr, -1, msg, LogLevel::Erro);
    return *this;
}
template<typename T>
typename std::enable_if<!std::is_same<std::string,
    typename std::decay<T>::type>::value, Logger>::type& Logger::e(
    T const& msg) noexcept
{
    std::stringstream ss;
    ss << msg;
    this->append(nullptr, nullptr, -1, ss.str(), LogLevel::Erro);
    return *this;
}
template<typename T>
typename std::enable_if<!std::is_same<std::string,
    typename std::decay<T>::type>::value, Logger>::type& Logger::e(
    std::string const& name, T const& msg) noexcept
{
    std::stringstream ss;
    ss << msg;
    this->append(name.c_str(), nullptr, -1, ss.str(), LogLevel::Erro);
    return *this;
}
template<uint32_t sz> Logger& Logger::e(char const(&msg)[sz]) noexcept
{
    this->append(nullptr, nullptr, -1, msg, LogLevel::Erro);
    return *this;
}
template<uint32_t sz> Logger& Logger::e(
    std::string const& name, char const(&msg)[sz]) noexcept
{
    this->append(name, nullptr, -1, msg, LogLevel::Erro);
    return *this;
}
// w
template<typename T>
typename std::enable_if<std::is_same<std::string,
    typename std::decay<T>::type>::value, Logger>::type& Logger::w(
    T const& msg) noexcept
{
    this->append(nullptr, nullptr, -1, msg, LogLevel::Warn);
    return *this;
}
template<typename T>
typename std::enable_if<std::is_same<std::string,
    typename std::decay<T>::type>::value, Logger>::type& Logger::w(
    std::string const& name, T const& msg) noexcept
{
    this->append(name.c_str(), nullptr, -1, msg, LogLevel::Warn);
    return *this;
}
template<typename T>
typename std::enable_if<!std::is_same<std::string,
    typename std::decay<T>::type>::value, Logger>::type& Logger::w(
    T const& msg) noexcept
{
    std::stringstream ss;
    ss << msg;
    this->append(nullptr, nullptr, -1, ss.str(), LogLevel::Warn);
    return *this;
}
template<typename T>
typename std::enable_if<!std::is_same<std::string,
    typename std::decay<T>::type>::value, Logger>::type& Logger::w(
    std::string const& name, T const& msg) noexcept
{
    std::stringstream ss;
    ss << msg;
    this->append(name.c_str(), nullptr, -1, ss.str(), LogLevel::Warn);
    return *this;
}
template<uint32_t sz> Logger& Logger::w(char const(&msg)[sz]) noexcept
{
    this->append(nullptr, nullptr, -1, msg, LogLevel::Warn);
    return *this;
}
template<uint32_t sz> Logger& Logger::w(
    std::string const& name, char const(&msg)[sz]) noexcept
{
    this->append(name, nullptr, -1, msg, LogLevel::Warn);
    return *this;
}
// n
template<typename T>
typename std::enable_if<std::is_same<std::string,
    typename std::decay<T>::type>::value, Logger>::type& Logger::n(
    T const& msg) noexcept
{
    this->append(nullptr, nullptr, -1, msg, LogLevel::Note);
    return *this;
}
template<typename T>
typename std::enable_if<std::is_same<std::string,
    typename std::decay<T>::type>::value, Logger>::type& Logger::n(
    std::string const& name, T const& msg) noexcept
{
    this->append(name.c_str(), nullptr, -1, msg, LogLevel::Note);
    return *this;
}
template<typename T>
typename std::enable_if<!std::is_same<std::string,
    typename std::decay<T>::type>::value, Logger>::type& Logger::n(
    T const& msg) noexcept
{
    std::stringstream ss;
    ss << msg;
    this->append(nullptr, nullptr, -1, ss.str(), LogLevel::Note);
    return *this;
}
template<typename T>
typename std::enable_if<!std::is_same<std::string,
    typename std::decay<T>::type>::value, Logger>::type& Logger::n(
    std::string const& name, T const& msg) noexcept
{
    std::stringstream ss;
    ss << msg;
    this->append(name.c_str(), nullptr, -1, ss.str(), LogLevel::Note);
    return *this;
}
template<uint32_t sz> Logger& Logger::n(char const(&msg)[sz]) noexcept
{
    this->append(nullptr, nullptr, -1, msg, LogLevel::Note);
    return *this;
}
template<uint32_t sz> Logger& Logger::n(
    std::string const& name, char const(&msg)[sz]) noexcept
{
    this->append(name, nullptr, -1, msg, LogLevel::Note);
    return *this;
}
// i
template<typename T>
typename std::enable_if<std::is_same<std::string,
    typename std::decay<T>::type>::value, Logger>::type& Logger::i(
    T const& msg) noexcept
{
    this->append(nullptr, nullptr, -1, msg, LogLevel::Info);
    return *this;
}
template<typename T>
typename std::enable_if<std::is_same<std::string,
    typename std::decay<T>::type>::value, Logger>::type& Logger::i(
    std::string const& name, T const& msg) noexcept
{
    this->append(name.c_str(), nullptr, -1, msg, LogLevel::Info);
    return *this;
}
template<typename T>
typename std::enable_if<!std::is_same<std::string,
    typename std::decay<T>::type>::value, Logger>::type& Logger::i(
    T const& msg) noexcept
{
    std::stringstream ss;
    ss << msg;
    this->append(nullptr, nullptr, -1, ss.str(), LogLevel::Info);
    return *this;
}
template<typename T>
typename std::enable_if<!std::is_same<std::string,
    typename std::decay<T>::type>::value, Logger>::type& Logger::i(
    std::string const& name, T const& msg) noexcept
{
    std::stringstream ss;
    ss << msg;
    this->append(name.c_str(), nullptr, -1, ss.str(), LogLevel::Info);
    return *this;
}
template<uint32_t sz> Logger& Logger::i(char const(&msg)[sz]) noexcept
{
    this->append(nullptr, nullptr, -1, msg, LogLevel::Info);
    return *this;
}
template<uint32_t sz> Logger& Logger::i(
    std::string const& name, char const(&msg)[sz]) noexcept
{
    this->append(name, nullptr, -1, msg, LogLevel::Info);
    return *this;
}
// d
template<typename T>
typename std::enable_if<std::is_same<std::string,
    typename std::decay<T>::type>::value, Logger>::type& Logger::d(
    T const& msg) noexcept
{
    this->append(nullptr, nullptr, -1, msg, LogLevel::Debu);
    return *this;
}
template<typename T>
typename std::enable_if<std::is_same<std::string,
    typename std::decay<T>::type>::value, Logger>::type& Logger::d(
    std::string const& name, T const& msg) noexcept
{
    this->append(name.c_str(), nullptr, -1, msg, LogLevel::Debu);
    return *this;
}
template<typename T>
typename std::enable_if<!std::is_same<std::string,
    typename std::decay<T>::type>::value, Logger>::type& Logger::d(
    T const& msg) noexcept
{
    std::stringstream ss;
    ss << msg;
    this->append(nullptr, nullptr, -1, ss.str(), LogLevel::Debu);
    return *this;
}
template<typename T>
typename std::enable_if<!std::is_same<std::string,
    typename std::decay<T>::type>::value, Logger>::type& Logger::d(
    std::string const& name, T const& msg) noexcept
{
    std::stringstream ss;
    ss << msg;
    this->append(name.c_str(), nullptr, -1, ss.str(), LogLevel::Debu);
    return *this;
}
template<uint32_t sz> Logger& Logger::d(char const(&msg)[sz]) noexcept
{
    this->append(nullptr, nullptr, -1, msg, LogLevel::Debu);
    return *this;
}
template<uint32_t sz> Logger& Logger::d(
    std::string const& name, char const(&msg)[sz]) noexcept
{
    this->append(name, nullptr, -1, msg, LogLevel::Debu);
    return *this;
}
// <<
template<typename T>
typename std::enable_if<std::is_same<std::string,
    typename std::decay<T>::type>::value, Logger>::type& Logger::operator<<(
    T const& msg) noexcept
{
    this->append(nullptr, nullptr, -1, msg, this->logLevel);
    return *this;
}
template<typename T>
typename std::enable_if<!std::is_same<std::string,
    typename std::decay<T>::type>::value, Logger>::type& Logger::operator<<(
    T const& msg) noexcept
{
    std::stringstream ss;
    ss << msg;
    this->append(nullptr, nullptr, -1, ss.str(), this->logLevel);
    return *this;
}
template<uint32_t sz>
Logger& Logger::operator<<(char const(&msg)[sz]) noexcept
{
    this->append(nullptr, nullptr, -1, msg, this->logLevel);
    return *this;
}
template<typename T>
typename std::enable_if<std::is_same<std::string,
    typename std::decay<T>::type>::value, Logger>::type& Logger::operator,(
    T const& msg) noexcept
{
    this->append(msg, this->logLevel);
    return *this;
}
template<typename T>
typename std::enable_if<!std::is_same<std::string,
    typename std::decay<T>::type>::value, Logger>::type& Logger::operator,(
    T const& msg) noexcept
{
    std::stringstream ss;
    ss << msg;
    this->append(ss.str(), this->logLevel);
    return *this;
}
template<uint32_t sz>
Logger& Logger::operator,(char const(&msg)[sz]) noexcept
{
    this->append(msg, this->logLevel);
    return *this;
}