easylogging++的那些事(四)源码分析(十四)其他工具类(一)

在上一篇文章中我们介绍完了 LogFormat 类,今天我们来看看还剩下的一些工具类当中的一部分。

Helpers 类

    Helpers 类为库的使用者提供的一些方便的接口。其实现如下:

/// @brief Static helpers for developers
class Helpers : base::StaticClass
{
public:
    // 设置全局管理对象
    /// @brief Shares logging repository (base::Storage)
    static inline void setStorage(base::type::StoragePointer storage)
    {
        ELPP = storage;
    }
    // 获取全局管理对象
    /// @return Main storage repository
    static inline base::type::StoragePointer storage()
    {
        return ELPP;
    }
    // 通过设置命令行参数来加载相关配置,如设置详细日志模块规则,加载LoggingFlag,全局配置默认日志文件名等
    /// @brief Sets application arguments and figures out whats active for logging and whats not.
    static inline void setArgs(int argc, char **argv)
    {
        ELPP->setApplicationArguments(argc, argv);
    }
    /// @copydoc setArgs(int argc, char** argv)
    static inline void setArgs(int argc, const char **argv)
    {
        ELPP->setApplicationArguments(argc, const_cast<char **>(argv));
    }
    /// @brief Sets thread name for current thread. Requires std::thread
    static inline void setThreadName(const std::string &name)
    {
        ELPP->setThreadName(name);
    }
    static inline std::string getThreadName()
    {
        return ELPP->getThreadName(base::threading::getCurrentThreadId());
    }
#if defined(ELPP_FEATURE_ALL) || defined(ELPP_FEATURE_CRASH_LOG)
    // 设置崩溃信号处理器
    /// @brief Overrides default crash handler and installs custom handler.
    /// @param crashHandler A functor with no return type that takes single int argument.
    ///        Handler is a typedef with specification: void (*Handler)(int)
    static inline void setCrashHandler(const el::base::debug::CrashHandler::Handler &crashHandler)
    {
        el::elCrashHandler.setHandler(crashHandler);
    }
    // 记录程序崩溃原因
    // 记录崩溃相关信息让后程序退出
    /// @brief Abort due to crash with signal in parameter
    /// @param sig Crash signal
    static void crashAbort(int sig, const char *sourceFile = "", unsigned int long line = 0);
    /// @brief Logs reason of crash as per sig
    /// @param sig Crash signal
    /// @param stackTraceIfAvailable Includes stack trace if available
    /// @param level Logging level
    /// @param logger Logger to use for logging
    static void logCrashReason(int sig, bool stackTraceIfAvailable = false,
                               Level level = Level::Fatal, const char *logger = base::consts::kDefaultLoggerId);
#endif // defined(ELPP_FEATURE_ALL) || defined(ELPP_FEATURE_CRASH_LOG)
    // 安装日志回旋回调(源码当中仅能用于备份,和实际项目需求有差异)
    /// @brief Installs pre rollout callback, this callback is triggered when log file is about to be rolled out
    ///        (can be useful for backing up)
    static inline void installPreRollOutCallback(const PreRollOutCallback &callback)
    {
        ELPP->setPreRollOutCallback(callback);
    }
    // 卸载日志回旋回调(恢复为默认的日志回旋回调)
    /// @brief Uninstalls pre rollout callback
    static inline void uninstallPreRollOutCallback(void)
    {
        ELPP->unsetPreRollOutCallback();
    }
    // 安装日志派发回调
    /// @brief Installs post log dispatch callback, this callback is triggered when log is dispatched
    template <typename T>
    static inline bool installLogDispatchCallback(const std::string &id)
    {
        return ELPP->installLogDispatchCallback<T>(id);
    }
    // 卸载日志派发回调
    /// @brief Uninstalls log dispatch callback
    template <typename T>
    static inline void uninstallLogDispatchCallback(const std::string &id)
    {
        ELPP->uninstallLogDispatchCallback<T>(id);
    }
    // 获取指定的日志派发回调对象指针
    template <typename T>
    static inline T *logDispatchCallback(const std::string &id)
    {
        return ELPP->logDispatchCallback<T>(id);
    }
#if defined(ELPP_FEATURE_ALL) || defined(ELPP_FEATURE_PERFORMANCE_TRACKING)
    // 安装性能跟踪回调
    /// @brief Installs post performance tracking callback, this callback is triggered when performance tracking is finished
    template <typename T>
    static inline bool installPerformanceTrackingCallback(const std::string &id)
    {
        return ELPP->installPerformanceTrackingCallback<T>(id);
    }
    // 卸载性能跟踪回调
    /// @brief Uninstalls post performance tracking handler
    template <typename T>
    static inline void uninstallPerformanceTrackingCallback(const std::string &id)
    {
        ELPP->uninstallPerformanceTrackingCallback<T>(id);
    }
    // 获取指定的性能跟踪回调对象指针
    template <typename T>
    static inline T *performanceTrackingCallback(const std::string &id)
    {
        return ELPP->performanceTrackingCallback<T>(id);
    }
// 将指定类型的对象转化为string
#endif // defined(ELPP_FEATURE_ALL) || defined(ELPP_FEATURE_PERFORMANCE_TRACKING)
    /// @brief Converts template to std::string - useful for loggable classes to log containers within log(std::ostream&) const
    template <typename T>
    static std::string convertTemplateToStdString(const T &templ)
    {
        el::Logger *logger =
            ELPP->registeredLoggers()->get(el::base::consts::kDefaultLoggerId);
        if (logger == nullptr)
        {
            return std::string();
        }
        base::MessageBuilder b;
        b.initialize(logger);
        logger->acquireLock();
        b << templ;
#if defined(ELPP_UNICODE)
        std::string s = std::string(logger->stream().str().begin(), logger->stream().str().end());
#else
        std::string s = logger->stream().str();
#endif // defined(ELPP_UNICODE)
        logger->stream().str(ELPP_LITERAL(""));
        logger->releaseLock();
        return s;
    }
    // 获取全局命令行参数解析器
    /// @brief Returns command line arguments (pointer) provided to easylogging++
    static inline const el::base::utils::CommandLineArgs *commandLineArgs(void)
    {
        return ELPP->commandLineArgs();
    }
    // 自定义日志格式解析器相关接口
    // 调整全局自定义日志格式解析器容器的大小
    /// @brief Reserve space for custom format specifiers for performance
    /// @see std::vector::reserve
    static inline void reserveCustomFormatSpecifiers(std::size_t size)
    {
        ELPP->m_customFormatSpecifiers.reserve(size);
    }
    // 安装自定义自定义日志格式解析器
    /// @brief Installs user defined format specifier and handler
    static inline void installCustomFormatSpecifier(const CustomFormatSpecifier &customFormatSpecifier)
    {
        ELPP->installCustomFormatSpecifier(customFormatSpecifier);
    }
    // 卸载自定义日志格式的解析器
    /// @brief Uninstalls user defined format specifier and handler
    static inline bool uninstallCustomFormatSpecifier(const char *formatSpecifier)
    {
        return ELPP->uninstallCustomFormatSpecifier(formatSpecifier);
    }
    // 查询是否含有自定义日志格式的解析器
    /// @brief Returns true if custom format specifier is installed
    static inline bool hasCustomFormatSpecifier(const char *formatSpecifier)
    {
        return ELPP->hasCustomFormatSpecifier(formatSpecifier);
    }

    // 执行日志回旋
    static inline void validateFileRolling(Logger *logger, Level level)
    {
        if (ELPP == nullptr || logger == nullptr)
            return;
        logger->m_typedConfigurations->validateFileRolling(level, ELPP->preRollOutCallback());
    }
};

// 记录崩溃相关信息让后程序退出
void Helpers::crashAbort(int sig, const char *sourceFile, unsigned int long line)
{
    std::stringstream ss;
    ss << base::debug::crashReason(sig).c_str();
    ss << " - [Called el::Helpers::crashAbort(" << sig << ")]";
    if (sourceFile != nullptr && strlen(sourceFile) > 0)
    {
        ss << " - Source: " << sourceFile;
        if (line > 0)
            ss << ":" << line;
        else
            ss << " (line number not specified)";
    }
    base::utils::abort(sig, ss.str());
}
// 记录程序崩溃原因
void Helpers::logCrashReason(int sig, bool stackTraceIfAvailable, Level level, const char *logger)
{
    el::base::debug::logCrashReason(sig, stackTraceIfAvailable, level, logger);
}

    Helpers 工具类仅仅是其他相关接口的简单包装而已,没什么好多说的。

Loggers 类

    Loggers 类是处理日志记录器和对应配置的工具类

已经介绍的接口

    在 日志格式配置方式 中我们介绍了日志配置相关的接口。相关接口声明如下:

Logger *reconfigureLogger(Logger *logger, const Configurations &configurations);
Logger *reconfigureLogger(const std::string &identity, const Configurations &configurations);
// 配置某个日志记录器的某个配置项
Logger *reconfigureLogger(const std::string &identity, ConfigurationType configurationType, const std::string &value);
// 配置所有日志记录器的某个配置项
void reconfigureAllLoggers(ConfigurationType configurationType, const std::string &value);
// 配置所有日志记录器的某个日志级别的某个配置项
void reconfigureAllLoggers(Level level, ConfigurationType configurationType, const std::string &value);
// 所有日志记录器的默认配置
void setDefaultConfigurations(const Configurations &configurations, bool reconfigureExistingLoggers = false);
const Configurations *defaultConfigurations(void);
base::TypedConfigurations defaultTypedConfigurations(void);
// 从全局配置文件中加载配置(仅仅支持带记录器ID这种形式的配置文件 如--default)
// 主要就是解析配置文件,一些文件流和字符串的操作比较多,这里就不一一解析每行代码干嘛用的了,整个代码可读性较好。
void configureFromGlobal(const char *globalConfigurationFilePath);
// 通过命令行参数配置
bool Loggers::configureFromArg(const char *argKey);

VERBOSE 日志信息管理 中我们介绍了 VERBOSE 日志信息管理相关的接口。相关接口声明如下:

void Loggers::setVerboseLevel(base::type::VerboseLevel level);
base::type::VerboseLevel Loggers::verboseLevel(void);
void Loggers::setVModules(const char *modules);
void Loggers::clearVModules(void);

日志记录器注册事件回调相关接口

// 安装回调日志记录器注册事件回调
/// @brief Installs logger registration callback, this callback is triggered when new logger is registered
template <typename T>
static inline bool installLoggerRegistrationCallback(const std::string &id)
{
    return ELPP->registeredLoggers()->installLoggerRegistrationCallback<T>(id);
}
// 卸载日志记录器注册事件回调
/// @brief Uninstalls log dispatch callback
template <typename T>
static inline void uninstallLoggerRegistrationCallback(const std::string &id)
{
    ELPP->registeredLoggers()->uninstallLoggerRegistrationCallback<T>(id);
}
// 获取日志记录器注册事件回调
template <typename T>
static inline T *loggerRegistrationCallback(const std::string &id)
{
    return ELPP->registeredLoggers()->loggerRegistrationCallback<T>(id);
}

获取日志记录器

Logger *Loggers::getLogger(const std::string &identity, bool registerIfNotAvailable)
{
    return ELPP->registeredLoggers()->get(identity, registerIfNotAvailable);
}

查询日志记录器是否存在

bool Loggers::hasLogger(const std::string &identity)
{
    return ELPP->registeredLoggers()->has(identity);
}

删除日志记录器

bool Loggers::unregisterLogger(const std::string &identity)
{
    return ELPP->registeredLoggers()->remove(identity);
}

统计所有的日志记录器的 ID

std::vector<std::string> *Loggers::populateAllLoggerIds(std::vector<std::string> *targetList)
{
    targetList->clear();
    for (base::RegisteredLoggers::iterator it = ELPP->registeredLoggers()->list().begin();
         it != ELPP->registeredLoggers()->list().end(); ++it)
    {
        targetList->push_back(it->first);
    }
    return targetList;
}

设置默认的日志构建器

void Loggers::setDefaultLogBuilder(el::LogBuilderPtr &logBuilderPtr)
{
    ELPP->registeredLoggers()->setDefaultLogBuilder(logBuilderPtr);
}

获取所有正在使用的日志文件对应的文件流容器

const base::LogStreamsReferenceMapPtr Loggers::logStreamsReference(void)
{
    return ELPP->registeredLoggers()->logStreamsReference();
}

// 刷新全部的日志文件
void Loggers::flushAll(void)
{
    ELPP->registeredLoggers()->flushAll();
}

LoggingFlag 相关接口

// 向全局LoggingFlag中追加指定LoggingFlag
/// @brief Adds logging flag used internally.
static inline void addFlag(LoggingFlag flag)
{
    ELPP->addFlag(flag);
}
// 向全局LoggingFlag中删除指定LoggingFlag
/// @brief Removes logging flag used internally.
static inline void removeFlag(LoggingFlag flag)
{
    ELPP->removeFlag(flag);
}
// 检测是全局LoggingFlag中否存在指定的LoggingFlag
/// @brief Determines whether or not certain flag is active
static inline bool hasFlag(LoggingFlag flag)
{
    return ELPP->hasFlag(flag);
}

// 提供的内部warpper类管理LoggingFlag
// 向LoggingFlag中临时添加LoggingFlag,离开ScopedAddFlag作用域时删除添加的LoggingFlag
/// @brief Adds flag and removes it when scope goes out
class ScopedAddFlag
{
public:
    ScopedAddFlag(LoggingFlag flag) : m_flag(flag)
    {
        Loggers::addFlag(m_flag);
    }
    ~ScopedAddFlag(void)
    {
        Loggers::removeFlag(m_flag);
    }

private:
    LoggingFlag m_flag;
};
// 从LoggingFlag中临时删除LoggingFlag,离开ScopedRemoveFlag作用域时添加删除的LoggingFlag
/// @brief Removes flag and add it when scope goes out
class ScopedRemoveFlag
{
public:
    ScopedRemoveFlag(LoggingFlag flag) : m_flag(flag)
    {
        Loggers::removeFlag(m_flag);
    }
    ~ScopedRemoveFlag(void)
    {
        Loggers::addFlag(m_flag);
    }

private:
    LoggingFlag m_flag;
};

设置全局分层日志级别的基准级别

// 全局分层日志级别的基准级别用于确定是否处理日志
/// @brief Sets hierarchy for logging. Needs to enable logging flag (HierarchicalLogging)
static void setLoggingLevel(Level level)
{
    ELPP->setLoggingLevel(level);
}

    Loggers 工具类仅仅是其他相关接口的简单包装而已,没什么好多说的。

回调相关函数模板接口

    下面三个函数模板被其他类型的回调使用,如:日志记录器注册事件回调、性能跟踪回调和日志派发回调的相关对应接口使用

// 安装回调
template <typename T, typename TPtr>
static bool installCallback(const std::string &id, std::unordered_map<std::string, TPtr> *mapT)
{
    if (mapT->find(id) == mapT->end())
    {
        mapT->insert(std::make_pair(id, TPtr(new T())));
        return true;
    }
    return false;
}

// 卸载回调
template <typename T, typename TPtr>
static void uninstallCallback(const std::string &id, std::unordered_map<std::string, TPtr> *mapT)
{
    if (mapT->find(id) != mapT->end())
    {
        mapT->erase(id);
    }
}

// 获取回调对象指针
template <typename T, typename TPtr>
static T *callback(const std::string &id, std::unordered_map<std::string, TPtr> *mapT)
{
    typename std::unordered_map<std::string, TPtr>::iterator iter = mapT->find(id);
    if (iter != mapT->end())
    {
        return static_cast<T *>(iter->second.get());
    }
    return nullptr;
}

Loggable 类

    自定义第三方类型可通过继承这个类,实现 log 接口来让 easylogging++支持直接日志记录这个类型的对象。

class Loggable
{
public:
    virtual ~Loggable(void) {}
    virtual void log(el::base::type::ostream_t &) const = 0;

private:
    friend inline el::base::type::ostream_t &operator<<(el::base::type::ostream_t &os, const Loggable &loggable)
    {
        // 内部通过调用实际类型的log接口来实现将指定Loggable的实例直接写日志
        loggable.log(os);
        return os;
    }
};

HitCounter 类

    HitCounter 类是指定文件的指定行的统计类

/// @brief Class that keeps record of current line hit for occasional logging
class HitCounter
{
public:
    HitCounter(void) : m_filename(""), m_lineNumber(0), m_hitCounts(0)
    {
    }

    HitCounter(const char *filename, base::type::LineNumber lineNumber)
        : m_filename(filename), m_lineNumber(lineNumber), m_hitCounts(0)
    {
    }

    HitCounter(const HitCounter &hitCounter)
        : m_filename(hitCounter.m_filename), m_lineNumber(hitCounter.m_lineNumber), m_hitCounts(hitCounter.m_hitCounts)
    {
    }

    HitCounter &operator=(const HitCounter &hitCounter)
    {
        if (&hitCounter != this)
        {
            m_filename = hitCounter.m_filename;
            m_lineNumber = hitCounter.m_lineNumber;
            m_hitCounts = hitCounter.m_hitCounts;
        }
        return *this;
    }

    virtual ~HitCounter(void)
    {
    }

    /// @brief Resets location of current hit counter
    inline void resetLocation(const char *filename, base::type::LineNumber lineNumber)
    {
        m_filename = filename;
        m_lineNumber = lineNumber;
    }

    /// @brief Validates hit counts and resets it if necessary
    inline void validateHitCounts(std::size_t n)
    {
        if (m_hitCounts >= base::consts::kMaxLogPerCounter)
        {
            m_hitCounts = (n >= 1 ? base::consts::kMaxLogPerCounter % n : 0);
        }
        ++m_hitCounts;
    }

    inline const char *filename(void) const
    {
        return m_filename;
    }

    inline base::type::LineNumber lineNumber(void) const
    {
        return m_lineNumber;
    }

    inline std::size_t hitCounts(void) const
    {
        return m_hitCounts;
    }

    inline void increment(void)
    {
        ++m_hitCounts;
    }

    // 内部类用作谓词
    class Predicate
    {
    public:
        Predicate(const char *filename, base::type::LineNumber lineNumber)
            : m_filename(filename),
              m_lineNumber(lineNumber)
        {
        }
        inline bool operator()(const HitCounter *counter)
        {
            return ((counter != nullptr) &&
                    (strcmp(counter->m_filename, m_filename) == 0) &&
                    (counter->m_lineNumber == m_lineNumber));
        }

    private:
        const char *m_filename;
        base::type::LineNumber m_lineNumber;
    };

private:
    const char *m_filename;              // 源文件名称
    base::type::LineNumber m_lineNumber; // 行号
    std::size_t m_hitCounts;             // 指定文件的指定行的统计次数
};

RegisteredHitCounters 类

    RegisteredHitCounters 类是 HitCounter 的管理类。
    公有继承自 base::utils::RegistryWithPred <base::HitCounter, base::HitCounter::Predicate>
    底层容器为 std::vector <base::HitCounter*>
    迭代器类型:

// 实际iterator就是std::vector<base::HitCounter*>::iterator
typedef typename RegistryWithPred<base::HitCounter, base::HitCounter::Predicate>::iterator iterator;
// 实际const_iterator就是std::vector<base::HitCounter*>::const_iterator
typedef typename RegistryWithPred<base::HitCounter, base::HitCounter::Predicate>::const_iterator const_iterator;

    base::utils::RegistryWithPred 这个类模板后面介绍 easylogging++的设计理念时会详细分析,这里就不多说了。

已经介绍的接口

    在 偶尔日志宏 中我们已经介绍了偶尔写日志的相关接口。相关接口声明如下:

// 统计次数是n的整数倍时返回true
/// @brief Validates counter for every N, i.e, registers new if does not exist otherwise updates original one
/// @return True if validation resulted in triggering hit. Meaning logs should be written everytime true is returned
bool validateEveryN(const char *filename, base::type::LineNumber lineNumber, std::size_t n);

// 统计次数大于n时返回true
/// @brief Validates counter for hits >= N, i.e, registers new if does not exist otherwise updates original one
/// @return True if validation resulted in triggering hit. Meaning logs should be written everytime true is returned
bool validateAfterN(const char *filename, base::type::LineNumber lineNumber, std::size_t n);

// 统计次数小于等于n次时返回true
/// @brief Validates counter for hits are <= n, i.e, registers new if does not exist otherwise updates original one
/// @return True if validation resulted in triggering hit. Meaning logs should be written everytime true is returned
bool validateNTimes(const char *filename, base::type::LineNumber lineNumber, std::size_t n);

获取指定文件和指定行对应的 HitCounter

inline const base::HitCounter *getCounter(const char *filename, base::type::LineNumber lineNumber)
{
    base::threading::ScopedLock scopedLock(lock());
    return get(filename, lineNumber);
}

LogDispatchCallback 类

    LogDispatchCallback 类是日志派发回调的基类,每次记录日志后会触发。子类可以重写 handle 接口,以定制记录日志后会触发的动作。

class LogDispatchCallback : public Callback<LogDispatchData>
{
protected:
    virtual void handle(const LogDispatchData *data);
    base::threading::Mutex &fileHandle(const LogDispatchData *data);

private:
    friend class base::LogDispatcher;
    // 保存文件名和文件锁的unique_ptr对象的映射关系
    std::unordered_map<std::string, std::unique_ptr<base::threading::Mutex>> m_fileLocks;
    base::threading::Mutex m_fileLocksMapLock;
};

// ELPP_THREAD_SAFE宏:启用线程安全
#if defined(ELPP_THREAD_SAFE)
// 检查对应的日志消息对应日志级别对应的日志文件是否分配了文件锁,没有则分配一个
void LogDispatchCallback::handle(const LogDispatchData *data)
{
    base::threading::ScopedLock scopedLock(m_fileLocksMapLock);
    std::string filename = data->logMessage()->logger()->typedConfigurations()->filename(data->logMessage()->level());
    auto lock = m_fileLocks.find(filename);
    if (lock == m_fileLocks.end())
    {
        m_fileLocks.emplace(std::make_pair(filename, std::unique_ptr<base::threading::Mutex>(new base::threading::Mutex)));
    }
}
#else
void LogDispatchCallback::handle(const LogDispatchData * /*data*/)
{
}
#endif

// 根据日志文件名获取对应的文件锁
base::threading::Mutex &LogDispatchCallback::fileHandle(const LogDispatchData *data)
{
    auto it = m_fileLocks.find(data->logMessage()->logger()->typedConfigurations()->filename(data->logMessage()->level()));
    return *(it->second.get());
}

SysLogInitializer 类

/// @brief Initializes syslog with process ID, options and facility. calls closelog() on d'tor
class SysLogInitializer
{
public:
    SysLogInitializer(const char *processIdent, int options = 0, int facility = 0)
    {
#if defined(ELPP_SYSLOG)
        (void)base::consts::kSysLogLoggerId;
        openlog(processIdent, options, facility);
#else
        ELPP_UNUSED(processIdent);
        ELPP_UNUSED(options);
        ELPP_UNUSED(facility);
#endif // defined(ELPP_SYSLOG)
    }
    virtual ~SysLogInitializer(void)
    {
#if defined(ELPP_SYSLOG)
        closelog();
#endif // defined(ELPP_SYSLOG)
    }
};

    基于 RAII 方式对于 syslog 日志系统 API 的封装,也不复杂,就不多说了。
    用于 ELPP_INITIALIZE_SYSLOG 宏中:

#define ELPP_INITIALIZE_SYSLOG(id, opt, fac) el::SysLogInitializer elSyslogInit(id, opt, fac)

至此,相关工具类的介绍今天就到这里为止,下一篇我们继续介绍其他工具类。

posted @ 2022-12-09 12:56  节奏自由  阅读(128)  评论(0编辑  收藏  举报