一个简单实用的 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;
}

Auto startup by service

Auto startup by service

#

Example

  • Notes
    • ### BEGIN INIT INFO + ... + ### END INIT INFO 最好有,
      否则会有一个警告
    • 如果不在主分区, 注意等待分区或者文件系统挂载!
  • E.g. https://yuiwong.org/gitlab/pkgman/debrepo + ubuntu xenial
#!/bin/sh
### BEGIN INIT INFO
# Provides:          yuiwonguploadinotify
# Required-Start:    $local_fs $remote_fs $network $syslog
# Required-Stop:     $local_fs $remote_fs $network $syslog
# Default-Start:     1 2 3 4 5
# Default-Stop:      0 6
# Short-Description: My upload inotify
# Description:       My upload inotify.
### END INIT INFO
PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
HAS_DEBIAN=true
HAS_UBUNTU=true
prog="/opt/debrepo/bin/yuiwonguploadinotify"
#if [ -f /etc/default/rcS ]; then
#  . /etc/default/rcS
#fi
#. /lib/lsb/init-functions
case "$1" in
start)
  #killall yuiwonguploadinotify
  # Wait mount
  i=0
  echo "Wait mount .."
  #log_progress_msg "Wait mount .."
  while [ $i -lt 60 ]; do
    if [ -e "$prog" ]; then
      break
    fi
    sleep 1
    i=$(($i+1))
  done
  if [ ! -e "$prog" ]; then
    #log_progress_msg "Mount timed out"
    echo "Mount timed out" >&2
    exit 1
  fi
  #log_progress_msg "Mounted"
  echo "Mounted"
  if [ "true" = "${HAS_UBUNTU}" ]; then
    "$prog" yuiwong /my/debrepo/.ubuntuincoming \
      /my/debrepo/tools/reprepro-import_ubuntu /opt/debrepo/ubuntu-upload.log &
  fi
  if [ "true" = "${HAS_DEBIAN}" ]; then
    "$prog" yuiwong /my/debrepo/.debianincoming \
      /my/debrepo/tools/reprepro-import_debian /opt/debrepo/debian-upload.log &
  fi
  ;;
stop)
  killall yuiwonguploadinotify
  exit 0
  ;;
restart)
  $0 stop
  sleep 1
  $0 start
  ;;
*)
  echo "Usage: $0 {start|stop|restart}" >&2
  exit 1
  ;;
esac
exit 0

Install

E.g.

install_service: build
ifeq ($(REPO_ROOT_PATH),)
    @echo "No REPO_ROOT_PATH=..." > /dev/stderr
    @exit 1
endif
ifeq ($(IMPORTER_USER),)
    @echo "No IMPORTER_USER=..." > /dev/stderr
    @exit 1
endif
    @/bin/cp -f yuiwonguploadinotifyd.in yuiwonguploadinotifyd
    sed -i "s/REPO_ROOT_PATH/$$(echo -n $(REPO_ROOT_PATH) | sed 's/\//\\\//g')/g" yuiwonguploadinotifyd
    sed -i "s/IMPORTER_USER/$$(echo -n $(IMPORTER_USER) | sed 's/\//\\\//g')/g" yuiwonguploadinotifyd
    chmod a+x yuiwonguploadinotifyd
    cat yuiwonguploadinotifyd
    sudo cp yuiwonguploadinotifyd /etc/init.d/
    cd /etc/init.d/ && sudo update-rc.d yuiwonguploadinotifyd defaults 95

Uninstall

E.g.

uninstall_service:
    @test -n /etc/init.d/yuiwonguploadinotifyd \
    || sudo service yuiwonguploadinotifyd stop \
    && sudo update-rc.d -f yuiwonguploadinotifyd remove \
    && sudo rm -f yuiwonguploadinotifyd /etc/init.d/yuiwonguploadinotifyd \
    && sudo systemctl daemon-reload
    @sudo killall yuiwonguploadinotify || true

https://yuiwong.org/gitlab/computerscience/use-unix

Observer pattern

Observer 模式 (观察者模式 / 发布-订阅)

一个发布者(主题), 多个订阅者(观察者), 发布者发布消息通知所有订阅者.

例子
– 消息等发布-订阅
– MVC 是观察者模式的一种实现
– 常见的一个例子: 对同一组数据进行统计分析时候, 希望提供多种形式的表示,
(例如以表格进行统计显示, 柱状图统计显示和百分比统计显示等).
这些表示都依赖于同一组数据, 当数据改变的时候, 所有的统计的显示都能够改变.

认识观察者模式

认识观察者模式:

发布者(主题) + 订阅者(观察者) = 观察者模式:

观察者模式:

定义观察者模式

观察者模式 Observer pattern 定义了对象之间的一 (Subject or Topic) 对
多 (Observers) 的依赖关系.
这样一来, 当主题对象改变状态时, 它的所有依赖者都会收到通知并自动更新.

观察者模式 也称为发布-订阅 (Publish-Subscribe),
“主题 Subject” 就是消息的发布者(Publisher), “观察者 Observers”则是消息的订阅者
(Subscribers)

观察者模式应该可以说是应用最多, 影响最广的模式之一,
也是在大型系统开发过程中要用到的模式之一.

观察者的一个实例 Model / View / Control (MVC) 结构在系统开发架构设计中有着很重要的
地位和意义, MVC 实现了业务逻辑和表示层的解耦.
在 Java Struts 则提供和 MFC 中 Doc/View 结构类似的实现 MVC 的框架,
另外 Java 语言本身就提供了观察者模式的实现接口.

观察者模式类图示例


实现示例 1 分析

https://yuiwong.org/gitlab/cpp/cppprogdesipatt/tree/master/src/observer/example

/**
 * 简单的观察者模式(主题 即发布者, 观察者即订阅者)实现
 * g++ observer.cpp -std=c++11 -Wall -Wextra
 */
#include <iostream>
#include <memory>
#include <functional>
#include <set>
template<typename T> struct Observer;
/**
 * @struct Subject
 * A generic subject(i.e. publisher) implementation
 */
template<typename T>
struct Subject {
    using ObserverConstPtr = std::shared_ptr<Observer<T> const>;
    Subject() noexcept = default;
    virtual ~Subject() noexcept = default;
    virtual bool subscribe(ObserverConstPtr const& observer) noexcept;
    virtual bool unsubscribe(ObserverConstPtr const& observer) noexcept;
    virtual bool publish(std::shared_ptr<T const> const& message) const
    noexcept;
protected:
    virtual void notify() const noexcept;
    std::set<ObserverConstPtr> observers;
    mutable std::shared_ptr<T const> message;
};
/**
 * @struct Observer
 * Basic and generic observer(i.e. subscriber) implementation
 */
template<typename T>
struct Observer {
    Observer(
        std::function<void(std::shared_ptr<T const> const&)> const& callback)
        noexcept;
protected:
    Observer() noexcept = default;
public:
    virtual ~Observer() noexcept = default;
    virtual void update(std::shared_ptr<T const> const& message) const
    noexcept;
protected:
    std::function<void(std::shared_ptr<T const> const&)> callback;
};
template<typename T>
bool Subject<T>::subscribe(ObserverConstPtr const& observer) noexcept
{
    if (observer) {
        auto const ret = this->observers.insert(observer);
        return ret.second;
    }
    return false;
}
template<typename T>
bool Subject<T>::unsubscribe(ObserverConstPtr const& observer) noexcept
{
    auto const it = this->observers.find(observer);
    if (it != this->observers.end()) {
        this->observers.erase(observer);
        return true;
    }
    return false;
}
template<typename T>
bool Subject<T>::publish(std::shared_ptr<T const> const& message) const
noexcept
{
    if (message) {
        this->message = message;
        this->notify();
        return true;
    }
    return false;
}
template<typename T>
void Subject<T>::notify() const noexcept
{
    for (auto it = this->observers.cbegin(), end = this->observers.cend();
        it != end; ++it) {
        (*it)->update(this->message);
    }
}
template<typename T>
Observer<T>::Observer(
    std::function<void(std::shared_ptr<T const> const&)> const& callback)
    noexcept: callback(callback) {}
template<typename T>
void Observer<T>::update(std::shared_ptr<T const> const& message) const
noexcept
{
    if (this->callback) {
        this->callback(message);
    }
}
/// String subscriber
struct SSubsciber {
    SSubsciber();
    void callback(std::shared_ptr<std::string const> const& a);
protected:
    static int nextIdx;
    int idx;
};
int SSubsciber::nextIdx = 0;
SSubsciber::SSubsciber(): idx(SSubsciber::nextIdx++) {}
void SSubsciber::callback(std::shared_ptr<std::string const> const& a)
{
    std::cout << "SSubsciber." << this->idx << ": " << *a << "\n";
}
struct SObserver: public Observer<std::string> {
    SObserver(): Observer() {
        this->Observer::callback = std::bind(
            &SObserver::callback, this, std::placeholders::_1);
    }
    void callback(std::shared_ptr<std::string const> const& a) const {
        std::cout << "SObserver: " << *a << "\n";
    }
};
struct S2Observer: public Observer<std::string> {
    S2Observer(): Observer() {
        this->Observer::callback = std::bind(
            &S2Observer::callback, this, std::placeholders::_1);
    }
    void callback(std::shared_ptr<std::string const> const& a) const {
        std::cout << "S2Observer: " << *a << "\n";
    }
};
int main()
{
    Subject<std::string> sPublihser;
    std::shared_ptr<Observer<std::string> const> const sSubsciber1(
        new Observer<std::string>([](std::shared_ptr<
        std::string const> const& a){
            std::cout << "aSubsciber1: " << *a << "\n";
        }));
    std::shared_ptr<Observer<std::string> const> const sSubsciber2(
        new Observer<std::string>([](std::shared_ptr<
        std::string const> const& a){
            std::cout << "aSubsciber2: " << *a << "\n";
        }));
    SSubsciber ss3;
    SSubsciber ss4;
    std::shared_ptr<Observer<std::string> const> const s3(
        new Observer<std::string>(std::bind(
        &SSubsciber::callback, &ss3, std::placeholders::_1)));
    std::shared_ptr<Observer<std::string> const> const s4(
        new Observer<std::string>(std::bind(
        &SSubsciber::callback, &ss4, std::placeholders::_1)));
    std::shared_ptr<SObserver const> const s5(new SObserver());
    std::shared_ptr<S2Observer const> const s6(new S2Observer());
    sPublihser.subscribe(sSubsciber1);
    sPublihser.subscribe(sSubsciber2);
    sPublihser.subscribe(s3);
    sPublihser.subscribe(s4);
    sPublihser.subscribe(s5);
    sPublihser.subscribe(s6);
    auto const msg1 = std::shared_ptr<std::string>(new std::string("aaa"));
    auto const msg2 = std::shared_ptr<std::string>(new std::string("bbb"));
    sPublihser.publish(msg1);
    sPublihser.publish(msg2);
    std::cout << "\n";
    sPublihser.unsubscribe(sSubsciber1);
    sPublihser.publish(msg1);
    sPublihser.publish(msg2);
    return 0;
}

这里的主题提供依赖于它的观察者的注册和注销操作,
并且提供了使得依赖于它的所有观察者的通知操作. 观察者则提供一个 update 操作.

在观察者模式的示例实现中主题维护一个 unordered_set 作为存储其所有观察者的容器,
每当调用 notify 操作就遍历 set 中的观察者对象, 广播通知观察者 (调用观察者的 update).

运行示例程序, 可以看到当主题发布 aaa 和 bbb 的时候,
所有观察者都收到了 aaa 和 bbb, 然后第一个观察者取消订阅后,
再次发布 aaa 和 bbb 时除了第一个观察者其他都收到了.