easylogging++的那些事(四)源码分析(二)日志记录宏(一)CLOG宏(二)writer对象的创建以及初始化()

  • 构造函数
  • construct 接口
  • initializeLogger接口

在 easylogging++的那些事(四)源码分析(二)日志记录宏(一)CLOG宏(一)宏展开中我们对CLOG宏展开了,今天来看看日志记录宏中 CLOG 宏的实现。

从上面所有用户日志相关日志级别宏的最终展开结果可以看到: 都是创建了 类的实例,还是个临时对象。今天我们就来看看这个对象的创建过程。

el:: base:: Writer

类是日志记录的入口点。

el:: base:: Writer

构造函数

的构造函数的定义:
Writer(Level level, const char* file, base::type::LineNumber line,
const char* func, base::DispatchAction dispatchAction = base::DispatchAction::NormalLog,
base::type::VerboseLevel verboseLevel = 0) :
m_msg(nullptr), m_level(level), m_file(file), m_line(line), m_func(func), m_verboseLevel(verboseLevel),
m_logger(nullptr), m_proceed(false), m_dispatchAction(dispatchAction) {}

Writer(LogMessage* msg, base::DispatchAction dispatchAction = base::DispatchAction::NormalLog) :
m_msg(msg), m_level(msg != nullptr ? msg->level() : Level::Unknown),
m_line(0), m_logger(nullptr), m_proceed(false), m_dispatchAction(dispatchAction) {}

相关成员变量:
LogMessage* m_msg; //主要用于作为其他类的成员或者函数参数,日志相关信息的wrapper类
Logger* m_logger; //当前正在使用的日志记录器
bool m_proceed //当前的日志是否需要处理
base::MessageBuilder m_messageBuilder //用于支持各种类型的日志输出
base::DispatchAction m_dispatchAction //调度动作:当前是记录用户日志还是syslog日志
std::vector<std::string> m_loggerIds //当前日志所使用到的所有的日志记录器

构造函数本身只是初始化了一些成员变量,没有做太多事情。

的构造函数的定义:

el:: base:: Writer
    Writer(Level level, const char* file, base::type::LineNumber line,
        const char* func, base::DispatchAction dispatchAction = base::DispatchAction::NormalLog,
        base::type::VerboseLevel verboseLevel = 0) :
        m_msg(nullptr), m_level(level), m_file(file), m_line(line), m_func(func), m_verboseLevel(verboseLevel),
        m_logger(nullptr), m_proceed(false), m_dispatchAction(dispatchAction) {}

    Writer(LogMessage* msg, base::DispatchAction dispatchAction = base::DispatchAction::NormalLog) :
        m_msg(msg), m_level(msg != nullptr ? msg->level() : Level::Unknown),
        m_line(0), m_logger(nullptr), m_proceed(false), m_dispatchAction(dispatchAction) {}

相关成员变量:

    LogMessage* m_msg;   //主要用于作为其他类的成员或者函数参数,日志相关信息的wrapper类
    Logger* m_logger; //当前正在使用的日志记录器
    bool m_proceed //当前的日志是否需要处理
    base::MessageBuilder m_messageBuilder  //用于支持各种类型的日志输出
    base::DispatchAction m_dispatchAction  //调度动作:当前是记录用户日志还是syslog日志
    std::vector<std::string> m_loggerIds //当前日志所使用到的所有的日志记录器

构造函数本身只是初始化了一些成员变量,没有做太多事情。

construct 接口

的 接口的声明:
Writer& construct(Logger* logger, bool needLock = true);
Writer& construct(int count, const char* loggerIds, …);

根据 C++函数重载的匹配规则,是下面这个函数匹配上了:
Writer& construct(int count, const char* loggerIds, …);

这个函数的定义如下:
Writer &Writer::construct(int count, const char *loggerIds, …)
{
// 判断是否支持多日志记录器输出,比如:使用形式:LOG(INFO, “default”, “business”);
// 其中default和business是日志记录器ID,这样会分别使用default和business这两个日志记录器输出一条日志
if (ELPP->hasFlag(LoggingFlag::MultiLoggerSupport))
{
// 正常可变参部分全是字符串类型,代表的是多个日志记录器
va_list loggersList;
va_start(loggersList, loggerIds);
const char *id = loggerIds;
// m_loggerIds是std::vector<std::string>,先调用reserve预留足够的空间可以避免vector动态扩容带来的性能损失
m_loggerIds.reserve(count);
for (int i = 0; i < count; ++i)
{
// 将可变参列表中的每个参数(日志记录器)依次加入m_loggerIds
m_loggerIds.push_back(std::string(id));
id = va_arg(loggersList, const char *);
}
va_end(loggersList);
// 使用m_loggerIds当中的第一个记录器ID来初始化m_logger
initializeLogger(m_loggerIds.at(0));
}
else
{
// 如果不支持多日志记录器输出的话,直接使用loggerIds来初始化m_logger
initializeLogger(std::string(loggerIds));
}
// 初始化m_messageBuilder
m_messageBuilder.initialize(m_logger);
return *this;
}

的 接口的声明:

el:: base:: Writer
construct
    Writer& construct(Logger* logger, bool needLock = true);
    Writer& construct(int count, const char* loggerIds, ...);

根据 C++函数重载的匹配规则,是下面这个函数匹配上了:

    Writer& construct(int count, const char* loggerIds, ...);

这个函数的定义如下:

    Writer &Writer::construct(int count, const char *loggerIds, ...)
    {
        // 判断是否支持多日志记录器输出,比如:使用形式:LOG(INFO, "default", "business");
        // 其中default和business是日志记录器ID,这样会分别使用default和business这两个日志记录器输出一条日志
        if (ELPP->hasFlag(LoggingFlag::MultiLoggerSupport))
        {
            // 正常可变参部分全是字符串类型,代表的是多个日志记录器
            va_list loggersList;
            va_start(loggersList, loggerIds);
            const char *id = loggerIds;
            // m_loggerIds是std::vector<std::string>,先调用reserve预留足够的空间可以避免vector动态扩容带来的性能损失
            m_loggerIds.reserve(count);
            for (int i = 0; i < count; ++i)
            {
                // 将可变参列表中的每个参数(日志记录器)依次加入m_loggerIds
                m_loggerIds.push_back(std::string(id));
                id = va_arg(loggersList, const char *);
            }
            va_end(loggersList);
            // 使用m_loggerIds当中的第一个记录器ID来初始化m_logger
            initializeLogger(m_loggerIds.at(0));
        }
        else
        {
            // 如果不支持多日志记录器输出的话,直接使用loggerIds来初始化m_logger
            initializeLogger(std::string(loggerIds));
        }
        // 初始化m_messageBuilder
        m_messageBuilder.initialize(m_logger);
        return *this;
    }

initializeLogger接口

再来看看 的实现。
的声明如下:
void initializeLogger(const std::string& loggerId, bool lookup = true, bool needLock = true);

的定义如下:
void Writer::initializeLogger(const std::string &loggerId, bool lookup, bool needLock)
{
if (lookup)
{
// 如果需要查找日志记录器的话,就先查找对应ID的日志记录器
m_logger = ELPP->registeredLoggers()->get(loggerId, ELPP->hasFlag >> (LoggingFlag::CreateLoggerAutomatically));
}

if (m_logger == nullptr)
{
// 当对应ID的日志记录器不存在时,获取默认的日志记录器
{
if (!ELPP->registeredLoggers()->has(std::string >> (base::consts::kDefaultLoggerId)))
{
// Somehow default logger has been unregistered. Not good! Register again
ELPP->registeredLoggers()->get(std::string(base::consts::kDefaultLoggerId));
}
}
// 利用默认的日志记录器记录调试日志
Writer(Level::Debug, m_file, m_line, m_func).construct(1, >> base::consts::kDefaultLoggerId)
<< “Logger [” << loggerId << “] is not registered yet!”;
// 然后设置这条日志不处理
m_proceed = false;
}
else
{
// 当对应ID的日志记录器存在时
if (needLock)
{
// 当m_logger处理完这条日志后,会自动进行解锁
m_logger->acquireLock(); // This should not be unlocked by checking m_proceed >>because
// m_proceed can be changed by lines below
}
if (ELPP->hasFlag(LoggingFlag::HierarchicalLogging))
{
// 当分层日志记录时,日志为详细日志时,根据日志记录器对应的日志级别的启用配置来决>>定是当前的日志记录器否处理这条日志
// 当日志不为详细日志时,当当前的日志级别大于或者等于全局基准日志级别>>(ELPP->m_loggingLevel)才处理日志
m_proceed = m_level == Level::Verbose ? m_logger->enabled(m_level) : LevelHelper::castToInt(m_level) >= LevelHelper::castToInt (ELPP->m_loggingLevel);
}
else
{
// 当不分层日志记录时,根据日志记录器对应的日志级别的启用配置来决定当前的日志记录>>器是否处理这条日志
m_proceed = m_logger->enabled(m_level);
}
}
}

再来看看 的实现。
的声明如下:

initializeLogger
initializeLogger
    void initializeLogger(const std::string& loggerId, bool lookup = true, bool needLock = true);

的定义如下:

initializeLogger
    void Writer::initializeLogger(const std::string &loggerId, bool lookup, bool needLock)
    {
        if (lookup)
        {
            // 如果需要查找日志记录器的话,就先查找对应ID的日志记录器
            m_logger = ELPP->registeredLoggers()->get(loggerId, ELPP->hasFlag >> (LoggingFlag::CreateLoggerAutomatically));
        }
    
        if (m_logger == nullptr)
        {
            // 当对应ID的日志记录器不存在时,获取默认的日志记录器
            {
                if (!ELPP->registeredLoggers()->has(std::string >> (base::consts::kDefaultLoggerId)))
                {
                    // Somehow default logger has been unregistered. Not good! Register again
                    ELPP->registeredLoggers()->get(std::string(base::consts::kDefaultLoggerId));
                }
            }
            // 利用默认的日志记录器记录调试日志
            Writer(Level::Debug, m_file, m_line, m_func).construct(1, >> base::consts::kDefaultLoggerId)
                << "Logger [" << loggerId << "] is not registered yet!";
            // 然后设置这条日志不处理
            m_proceed = false;
        }
        else
        {
            // 当对应ID的日志记录器存在时
            if (needLock)
            {
                // 当m_logger处理完这条日志后,会自动进行解锁
                m_logger->acquireLock(); // This should not be unlocked by checking m_proceed >>because
                                         // m_proceed can be changed by lines below
            }
            if (ELPP->hasFlag(LoggingFlag::HierarchicalLogging))
            {
                // 当分层日志记录时,日志为详细日志时,根据日志记录器对应的日志级别的启用配置来决>>定是当前的日志记录器否处理这条日志
                // 当日志不为详细日志时,当当前的日志级别大于或者等于全局基准日志级别>>(ELPP->m_loggingLevel)才处理日志
                m_proceed = m_level == Level::Verbose ? m_logger->enabled(m_level) : LevelHelper::castToInt(m_level) >= LevelHelper::castToInt (ELPP->m_loggingLevel);
            }
            else
            {
                // 当不分层日志记录时,根据日志记录器对应的日志级别的启用配置来决定当前的日志记录>>器是否处理这条日志
                m_proceed = m_logger->enabled(m_level);
            }
        }
    }

到这里为止, 类对象的创建和初始化就介绍完了。

el:: base:: Writer

但我们发现了一个问题,自始至终没有看到真正写文件或者输出到终端或者输出到其他日志目的地的动作,那真实写日志的动作是在哪里触发的呢? 下一篇将为你揭晓。

————————
  • 构造函数
  • construct 接口
  • initializeLogger接口

在 easylogging++的那些事(四)源码分析(二)日志记录宏(一)CLOG宏(一)宏展开中我们对CLOG宏展开了,今天来看看日志记录宏中 CLOG 宏的实现。

从上面所有用户日志相关日志级别宏的最终展开结果可以看到: 都是创建了 类的实例,还是个临时对象。今天我们就来看看这个对象的创建过程。

el:: base:: Writer

类是日志记录的入口点。

el:: base:: Writer

构造函数

的构造函数的定义:
Writer(Level level, const char* file, base::type::LineNumber line,
const char* func, base::DispatchAction dispatchAction = base::DispatchAction::NormalLog,
base::type::VerboseLevel verboseLevel = 0) :
m_msg(nullptr), m_level(level), m_file(file), m_line(line), m_func(func), m_verboseLevel(verboseLevel),
m_logger(nullptr), m_proceed(false), m_dispatchAction(dispatchAction) {}

Writer(LogMessage* msg, base::DispatchAction dispatchAction = base::DispatchAction::NormalLog) :
m_msg(msg), m_level(msg != nullptr ? msg->level() : Level::Unknown),
m_line(0), m_logger(nullptr), m_proceed(false), m_dispatchAction(dispatchAction) {}

相关成员变量:
LogMessage* m_msg; //主要用于作为其他类的成员或者函数参数,日志相关信息的wrapper类
Logger* m_logger; //当前正在使用的日志记录器
bool m_proceed //当前的日志是否需要处理
base::MessageBuilder m_messageBuilder //用于支持各种类型的日志输出
base::DispatchAction m_dispatchAction //调度动作:当前是记录用户日志还是syslog日志
std::vector<std::string> m_loggerIds //当前日志所使用到的所有的日志记录器

构造函数本身只是初始化了一些成员变量,没有做太多事情。

的构造函数的定义:

el:: base:: Writer
    Writer(Level level, const char* file, base::type::LineNumber line,
        const char* func, base::DispatchAction dispatchAction = base::DispatchAction::NormalLog,
        base::type::VerboseLevel verboseLevel = 0) :
        m_msg(nullptr), m_level(level), m_file(file), m_line(line), m_func(func), m_verboseLevel(verboseLevel),
        m_logger(nullptr), m_proceed(false), m_dispatchAction(dispatchAction) {}

    Writer(LogMessage* msg, base::DispatchAction dispatchAction = base::DispatchAction::NormalLog) :
        m_msg(msg), m_level(msg != nullptr ? msg->level() : Level::Unknown),
        m_line(0), m_logger(nullptr), m_proceed(false), m_dispatchAction(dispatchAction) {}

相关成员变量:

    LogMessage* m_msg;   //主要用于作为其他类的成员或者函数参数,日志相关信息的wrapper类
    Logger* m_logger; //当前正在使用的日志记录器
    bool m_proceed //当前的日志是否需要处理
    base::MessageBuilder m_messageBuilder  //用于支持各种类型的日志输出
    base::DispatchAction m_dispatchAction  //调度动作:当前是记录用户日志还是syslog日志
    std::vector<std::string> m_loggerIds //当前日志所使用到的所有的日志记录器

构造函数本身只是初始化了一些成员变量,没有做太多事情。

construct 接口

的 接口的声明:
Writer& construct(Logger* logger, bool needLock = true);
Writer& construct(int count, const char* loggerIds, …);

根据 C++函数重载的匹配规则,是下面这个函数匹配上了:
Writer& construct(int count, const char* loggerIds, …);

这个函数的定义如下:
Writer &Writer::construct(int count, const char *loggerIds, …)
{
// 判断是否支持多日志记录器输出,比如:使用形式:LOG(INFO, “default”, “business”);
// 其中default和business是日志记录器ID,这样会分别使用default和business这两个日志记录器输出一条日志
if (ELPP->hasFlag(LoggingFlag::MultiLoggerSupport))
{
// 正常可变参部分全是字符串类型,代表的是多个日志记录器
va_list loggersList;
va_start(loggersList, loggerIds);
const char *id = loggerIds;
// m_loggerIds是std::vector<std::string>,先调用reserve预留足够的空间可以避免vector动态扩容带来的性能损失
m_loggerIds.reserve(count);
for (int i = 0; i < count; ++i)
{
// 将可变参列表中的每个参数(日志记录器)依次加入m_loggerIds
m_loggerIds.push_back(std::string(id));
id = va_arg(loggersList, const char *);
}
va_end(loggersList);
// 使用m_loggerIds当中的第一个记录器ID来初始化m_logger
initializeLogger(m_loggerIds.at(0));
}
else
{
// 如果不支持多日志记录器输出的话,直接使用loggerIds来初始化m_logger
initializeLogger(std::string(loggerIds));
}
// 初始化m_messageBuilder
m_messageBuilder.initialize(m_logger);
return *this;
}

的 接口的声明:

el:: base:: Writer
construct
    Writer& construct(Logger* logger, bool needLock = true);
    Writer& construct(int count, const char* loggerIds, ...);

根据 C++函数重载的匹配规则,是下面这个函数匹配上了:

    Writer& construct(int count, const char* loggerIds, ...);

这个函数的定义如下:

    Writer &Writer::construct(int count, const char *loggerIds, ...)
    {
        // 判断是否支持多日志记录器输出,比如:使用形式:LOG(INFO, "default", "business");
        // 其中default和business是日志记录器ID,这样会分别使用default和business这两个日志记录器输出一条日志
        if (ELPP->hasFlag(LoggingFlag::MultiLoggerSupport))
        {
            // 正常可变参部分全是字符串类型,代表的是多个日志记录器
            va_list loggersList;
            va_start(loggersList, loggerIds);
            const char *id = loggerIds;
            // m_loggerIds是std::vector<std::string>,先调用reserve预留足够的空间可以避免vector动态扩容带来的性能损失
            m_loggerIds.reserve(count);
            for (int i = 0; i < count; ++i)
            {
                // 将可变参列表中的每个参数(日志记录器)依次加入m_loggerIds
                m_loggerIds.push_back(std::string(id));
                id = va_arg(loggersList, const char *);
            }
            va_end(loggersList);
            // 使用m_loggerIds当中的第一个记录器ID来初始化m_logger
            initializeLogger(m_loggerIds.at(0));
        }
        else
        {
            // 如果不支持多日志记录器输出的话,直接使用loggerIds来初始化m_logger
            initializeLogger(std::string(loggerIds));
        }
        // 初始化m_messageBuilder
        m_messageBuilder.initialize(m_logger);
        return *this;
    }

initializeLogger接口

再来看看 的实现。
的声明如下:
void initializeLogger(const std::string& loggerId, bool lookup = true, bool needLock = true);

的定义如下:
void Writer::initializeLogger(const std::string &loggerId, bool lookup, bool needLock)
{
if (lookup)
{
// 如果需要查找日志记录器的话,就先查找对应ID的日志记录器
m_logger = ELPP->registeredLoggers()->get(loggerId, ELPP->hasFlag >> (LoggingFlag::CreateLoggerAutomatically));
}

if (m_logger == nullptr)
{
// 当对应ID的日志记录器不存在时,获取默认的日志记录器
{
if (!ELPP->registeredLoggers()->has(std::string >> (base::consts::kDefaultLoggerId)))
{
// Somehow default logger has been unregistered. Not good! Register again
ELPP->registeredLoggers()->get(std::string(base::consts::kDefaultLoggerId));
}
}
// 利用默认的日志记录器记录调试日志
Writer(Level::Debug, m_file, m_line, m_func).construct(1, >> base::consts::kDefaultLoggerId)
<< “Logger [” << loggerId << “] is not registered yet!”;
// 然后设置这条日志不处理
m_proceed = false;
}
else
{
// 当对应ID的日志记录器存在时
if (needLock)
{
// 当m_logger处理完这条日志后,会自动进行解锁
m_logger->acquireLock(); // This should not be unlocked by checking m_proceed >>because
// m_proceed can be changed by lines below
}
if (ELPP->hasFlag(LoggingFlag::HierarchicalLogging))
{
// 当分层日志记录时,日志为详细日志时,根据日志记录器对应的日志级别的启用配置来决>>定是当前的日志记录器否处理这条日志
// 当日志不为详细日志时,当当前的日志级别大于或者等于全局基准日志级别>>(ELPP->m_loggingLevel)才处理日志
m_proceed = m_level == Level::Verbose ? m_logger->enabled(m_level) : LevelHelper::castToInt(m_level) >= LevelHelper::castToInt (ELPP->m_loggingLevel);
}
else
{
// 当不分层日志记录时,根据日志记录器对应的日志级别的启用配置来决定当前的日志记录>>器是否处理这条日志
m_proceed = m_logger->enabled(m_level);
}
}
}

再来看看 的实现。
的声明如下:

initializeLogger
initializeLogger
    void initializeLogger(const std::string& loggerId, bool lookup = true, bool needLock = true);

的定义如下:

initializeLogger
    void Writer::initializeLogger(const std::string &loggerId, bool lookup, bool needLock)
    {
        if (lookup)
        {
            // 如果需要查找日志记录器的话,就先查找对应ID的日志记录器
            m_logger = ELPP->registeredLoggers()->get(loggerId, ELPP->hasFlag >> (LoggingFlag::CreateLoggerAutomatically));
        }
    
        if (m_logger == nullptr)
        {
            // 当对应ID的日志记录器不存在时,获取默认的日志记录器
            {
                if (!ELPP->registeredLoggers()->has(std::string >> (base::consts::kDefaultLoggerId)))
                {
                    // Somehow default logger has been unregistered. Not good! Register again
                    ELPP->registeredLoggers()->get(std::string(base::consts::kDefaultLoggerId));
                }
            }
            // 利用默认的日志记录器记录调试日志
            Writer(Level::Debug, m_file, m_line, m_func).construct(1, >> base::consts::kDefaultLoggerId)
                << "Logger [" << loggerId << "] is not registered yet!";
            // 然后设置这条日志不处理
            m_proceed = false;
        }
        else
        {
            // 当对应ID的日志记录器存在时
            if (needLock)
            {
                // 当m_logger处理完这条日志后,会自动进行解锁
                m_logger->acquireLock(); // This should not be unlocked by checking m_proceed >>because
                                         // m_proceed can be changed by lines below
            }
            if (ELPP->hasFlag(LoggingFlag::HierarchicalLogging))
            {
                // 当分层日志记录时,日志为详细日志时,根据日志记录器对应的日志级别的启用配置来决>>定是当前的日志记录器否处理这条日志
                // 当日志不为详细日志时,当当前的日志级别大于或者等于全局基准日志级别>>(ELPP->m_loggingLevel)才处理日志
                m_proceed = m_level == Level::Verbose ? m_logger->enabled(m_level) : LevelHelper::castToInt(m_level) >= LevelHelper::castToInt (ELPP->m_loggingLevel);
            }
            else
            {
                // 当不分层日志记录时,根据日志记录器对应的日志级别的启用配置来决定当前的日志记录>>器是否处理这条日志
                m_proceed = m_logger->enabled(m_level);
            }
        }
    }

到这里为止, 类对象的创建和初始化就介绍完了。

el:: base:: Writer

但我们发现了一个问题,自始至终没有看到真正写文件或者输出到终端或者输出到其他日志目的地的动作,那真实写日志的动作是在哪里触发的呢? 下一篇将为你揭晓。