muduo库的log

news2024/12/26 3:15:12

        muduo 库里的日志打印比较复杂,跟标准库 std::cout << 似乎比较像吧,库里自己实现了“流”式的日志打印,今天一起来看一下。

int main(int argc, char* argv[])
{
    CLogger::setLogLevel(CLogger::INFO);
    
    LOG_INFO << "loggingTest!";

    return 0;
}

这个就是一个简单的打印,首先设置了打印等级,然后就可以调用 LOG_INFO 或其他的宏进行打印。这些宏完整的定义如下:

#define LOG_TRACE if (CLogger::logLevel() <= CLogger::TRACE) \
  CLogger(__FILE__, __LINE__, CLogger::TRACE, __func__).stream()
#define LOG_DEBUG if (CLogger::logLevel() <= CLogger::DEBUG) \
  CLogger(__FILE__, __LINE__, CLogger::DEBUG, __func__).stream()
#define LOG_INFO if (CLogger::logLevel() <= CLogger::INFO) \
  CLogger(__FILE__, __LINE__).stream()
#define LOG_WARN CLogger(__FILE__, __LINE__, CLogger::WARN).stream()
#define LOG_ERROR CLogger(__FILE__, __LINE__, CLogger::ERROR).stream()
#define LOG_FATAL CLogger(__FILE__, __LINE__, CLogger::FATAL).stream()
#define LOG_SYSERR CLogger(__FILE__, __LINE__, false).stream()
#define LOG_SYSFATAL CLogger(__FILE__, __LINE__, true).stream()

截止到这里,是不是有人感到疑惑?因为从上面看,似乎没有调用什么接口进行输出啊,那它这个日志是怎么输出的呢?执行结果就是这样一句:

20230720 15:51:59.942668 tid: 13398 INFO  loggingTest! - LoggingTest.cpp:67

其实这里涉及到了一个知识点:匿名对象调用完就析构了。可以参考 这里。其实我们可以用 GDB 单步跟踪看它具体的执行步骤。如下:

调用到了 CLogger::logLevel() 获取当前的打印等级,其返回值是  CLogger::INFO,正是main函数开头设置的打印等级。

接着构造 CLogger 对象,调用的是这个构造函数:

CLogger::CLogger(SourceFile file, int line): mImpl(INFO, 0, file, line)
{
}

然而在这个构造函数里,首先去构造了 SourceFile 对象:

所以传递过来的 file 就是一个 SourceFile 对象。那紧接着就是构造 Impl 对象了。 

所以呢,LOG_INFO 宏得到的最终产物就是 mImpl.mStream,其类型就是 class CLogStream 

在得到 CLogStream 后,接着就是调用了重载符号 << 

 重载符号 << 里做的就是往 mBuffer 里添加内容,可以看到这个 mBuffer 的类型是一个模板类,

///固定缓存区类
template<int SIZE>
class CFixedBuffer: boost::noncopyable
{
public:
    CFixedBuffer(): mCur(mData)
    {
        setCookie(cookieStart);
    }

    ~CFixedBuffer()
    {
        setCookie(cookieEnd);
    }

    void append(const char* /*restrict*/ buf, size_t len)
    {
        // FIXME: append partially
        if (static_cast<size_t>(avail()) > len)
        {
            memcpy(mCur, buf, len);
            mCur += len;
        }
    }

    const char* data() const { return mData; }
    int length() const { return static_cast<int>(mCur - mData); }

    // write to mData directly
    char* current() { return mCur; }

    int avail() const { return static_cast<int>(end() - mCur); }

    void add(size_t len) { mCur += len; }

    void reset() { mCur = mData; }

    void bzero() { ::bzero(mData, sizeof mData); }

    // for used by GDB
    const char* debugString();

    void setCookie(void (*cookie)()) { mCookie = cookie; }

    // for used by unit test
    std::string asString() const { return std::string(mData, length()); }
private:
    const char* end() const { return mData + sizeof(mData); }

    // Must be outline function for cookies.
    static void cookieStart();
    static void cookieEnd();

    void (*mCookie)();

    char mData[SIZE];

    char* mCur;
};

 这个类里直接定义了 mData[SIZE] 大小的字符数组,这个 SIZE = 4000,所以这个mBuffer 可以容纳4000个字符。所以调用 mBuffer.append() 其实就是拷贝字符:

    void append(const char* /*restrict*/ buf, size_t len)
    {
        // FIXME: append partially
        if (static_cast<size_t>(avail()) > len)
        {
            memcpy(mCur, buf, len);
            mCur += len;
        }
    }

最后就调用到了 CLogger 的析构函数了:

析构函数里做了什么呢?

CLogger::~CLogger()
{
    mImpl.finish();
    const CLogStream::Buffer& buf(stream().buffer());
    g_output(buf.data(), buf.length());

    if (mImpl.mLevel == FATAL)
    {
        g_flush();
        abort();
    }
}

mImpl.finish() 里只是打印了文件名和行号,这个mBasename 和 mLine 在构造Impl的时候已经传递进去了:

void CLogger::Impl::finish()
{
    ///日志输出最后输出文件名、行号
    mStream << " - " << mBasename << ':' << mLine << '\n';
}

 然后用 g_output() 进行输出,输出的内容就是 stream().buffer()。而 g_output 是一个函数指针,其指向的是 defaultOutput() 函数,这个函数里就是向标准输出设备输出内容:

void defaultOutput(const char* msg, int len)
{
    size_t n = fwrite(msg, 1, len, stdout);
    //FIXME check n
    (void)n;
}

void defaultFlush()
{
    fflush(stdout);
}

CLogger::OutputFunc g_output = defaultOutput;
CLogger::FlushFunc g_flush = defaultFlush;

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/777446.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Xcode 15 beta 4 (15A5195m) - Apple 平台 IDE

Xcode 15 beta 4 (15A5195m) - Apple 平台 IDE IDE for iOS/iPadOS/macOS/watchOS/tvOS/visonOS 请访问原文链接&#xff1a;https://sysin.org/blog/apple-xcode-15/&#xff0c;查看最新版。原创作品&#xff0c;转载请保留出处。 作者主页&#xff1a;sysin.org visonOS …

07 QT信号与槽重载问题及解决

Tips&#xff1a; 参数重载时需要函数指针明确重载的是哪一个&#xff0c;避免出现二义性 案例修改&#xff1a; 重载学生类中的treat函数&#xff0c;增加foodName参数 //Teacher.h #ifndef TEACHER_H #define TEACHER_H#include <QObject>class Teacher : public QObje…

Jmeter安装及快速入门(超详细教程)

目录 1.安装Jmeter 1.1.下载 1.2.解压 1.3.运行 2.快速入门 2.1.设置中文语言 2.2.基本用法 1.安装Jmeter Jmeter依赖于JDK&#xff0c;所以必须确保当前计算机上已经安装了JDK&#xff0c;并且配置了环境变量。 1.1.下载 可以Apache Jmeter官网下载&#xff0c;地址&am…

18.matlab数据分析多项式的拟合(matlab程序)

1.简述 解决数据拟合问题最重要方法是最小二乘法和回归分析。如&#xff0c;我们需要从一组测定的数据&#xff08;例如N个点&#xff08;xi&#xff0c;yi&#xff09;&#xff08;i0,1&#xff0c;…&#xff0c;m&#xff09;&#xff09;去求得自变量 x 和因变量 y 的一个近…

解决Vue2中控制台报错 [WDS] Disconnected! 问题

Vue2 项目打开控制台时发现如下报错 &#xff1a; [WDS] Disconnected! client?9556:172 其实并没有对项目运行本身造成什么实质性的影响&#xff0c;但是一条红色的提示摆在那里确实不太好看&#xff0c;还是把他给解决掉吧。 在网上看好多人说需要将 config…

django实现好看的翻页分页效果,封装翻页组件,实现在任意页面实现翻页功能。

一、实现目标 在做web开发的时候,我们经常需要显示表格或者列表类型的数据,当数据比较多的时候,我们不会一次性全部显示出来,而是一次只显示固定数量的数据,当要查看其他数据时,点击翻页进行查看,而这个功能就是我们实现的分页、翻页功能。 话不多说,我们期望实现的翻页…

opencv-15 数字水印原理

最低有效位&#xff08;Least Significant Bit&#xff0c;LSB&#xff09;指的是一个二进制数中的第 0 位&#xff08;即最低位&#xff09;。 最低有效位信息隐藏指的是&#xff0c;将一个需要隐藏的二值图像信息嵌入载体图像的最低有效位&#xff0c;即将载体图像的最低有效…

央视报道!北京筑龙深度参编的 《企业采购供应链数字化成熟度模型》开始实施

7月12日&#xff0c;由中国物流与采购联合会牵头&#xff0c;北京筑龙深度参与编写的《企业采购供应链数字化成熟度模型》团体标准在“第四届国有企业数智化采购与智慧供应链论坛”上重磅发布。该标准于7月15日起正式实施&#xff0c;并将在企业中开展模型应用评估试点工作。 图…

服务器量化训练操作说明

Freespace服务器预训练主要步骤&#xff1a; 首先登录堡垒机&#xff0c;命令如下&#xff1a; ssh xxxrelay.baidu-int.com &#xff08;xxx为个人邮箱前缀&#xff09; 密码为个人邮箱密码 登录工作机&#xff0c;命令如下&#xff1a; ssh l3yq01-gpu-255-122-22-00.e…

Redis源码篇 - inset数据结构

inset是Redis中set类型的一种底层存储结构&#xff08;编码&#xff09;&#xff0c;它是基于整数数组来实现的&#xff0c;用于存储数值类型set集合数据&#xff0c;并具备长度可变、有序等特征。 有序性 为了方便查找&#xff0c;Redis会将intset中整数数据按照从小到大的顺…

017-从零搭建微服务-系统服务(四)

写在最前 如果这个项目让你有所收获&#xff0c;记得 Star 关注哦&#xff0c;这对我是非常不错的鼓励与支持。 源码地址&#xff08;后端&#xff09;&#xff1a;https://gitee.com/csps/mingyue 源码地址&#xff08;前端&#xff09;&#xff1a;https://gitee.com/csps…

【服务器】零成本搭建网站并内网穿透实现公网访问

零成本搭建网站并内网穿透实现公网访问 在普通电脑用户看来&#xff0c;建立自己的网站总是一件高大上的事情&#xff0c;这个网站不仅可以成为展示自己的平台&#xff0c;还能成为商业的载体。在以往&#xff0c;建立一个像样的网站&#xff0c;不仅需要过硬的编程知识做基础…

在外远程桌面控制家里的电脑

在外远程桌面控制家里的电脑 电脑作为现代家庭不可或缺的电子设备之一&#xff0c;早已在广大家庭中普及。不仅年轻人需要在工作、生活中使用电脑&#xff0c;在老家的父母也在熟悉和使用电脑。但身在外乡打工的我们&#xff0c;总会碰到家中父母抱怨电脑卡、慢、不好用的情况…

微信小程序学习笔记(四)——自定义组件

自定义组件 组件的创建与引用 创建组件 在根目录下创建 components 文件夹右键点击 components 文件夹&#xff0c;选择新建 Component&#xff0c;就会自动生成.wxml、.wxss、.js、.json文件 引用组件 组件的引用方式分为“局部引用”和“全局引用”&#xff0c;故名思义…

【指针的进阶(3)】回调函数和qsort排序各种类型的数据

文章目录 前言一、回调函数是什么&#xff1f;如何实现回调函数 二、回调函数的应用——qsortqsort排序各种类型的数据 总结 前言 前两章讲了指针的类型&#xff0c;数组传参和指针传参&#xff0c;还有函数指针和函数指针数组&#xff0c;接下来第三章讲回调函数 指针函数非常…

MySQL-运维

1、日志 1.1 错误日志 错误日志是MysQL中最重要的日志之一&#xff0c;它记录了当mysqld启动和停止时&#xff0c;以及服务器在运行过程中发生任何严重错误时的相关信息。当数据库出现任何故障导致无法正常使用时&#xff0c;建议首先查看此日志。 该日志是默认开启的&#…

高阶C语言|结构体,枚举,联合--自定义类型的使用计算

自定义类型--结构体&#xff0c;枚举&#xff0c;联合 一、结构体1.1结构体类型的声明1.1.1结构的基础知识1.1.2结构的声明1.1.3特殊的声明 1.2结构体的引用1.3结构体变量的定义和初始化1.4结构体内存对齐1.4.1修改默认对齐数 1.5结构体传参1.6结构体实现位段&#xff08;位段的…

数据结构与算法基础-学习-26-图之MST(最小代价生成树)之Kluskal(克鲁斯卡尔)算法

最小生成树的概念、源码实现和Prim&#xff08;普利姆&#xff09;算法的概念和源码实现请参考之前的博客&#xff1a;《数据结构与算法基础-学习-25-图之MST&#xff08;最小代价生成树&#xff09;之Prim&#xff08;普利姆&#xff09;算法》 一、算法思路 Kluskal算法相较…

11.键盘事件

键盘事件 html部分 <div class"insert"><div class"key">请按下你的键盘</div> </div>css部分 * {margin: 0;padding: 0; }body {display: flex;justify-content: center;align-items: center;height: 100vh;overflow: hidden; }…

Pytorch手动实现softmax回归

参考代码&#xff1a;https://blog.csdn.net/ccyyll1/article/details/126020585 softmax回归梯度计算方式&#xff0c;特别是ij和i! j时的计算问题&#xff0c;请看如下帖子中的描述&#xff0c;这个问题是反向传播梯度计算中的一个核心问题&#xff1a;反向传播梯度计算中的…