muduo库的log

文章详细介绍了muduo库中日志打印的实现方式,通过CLogger类和宏定义实现类似std::cout的流式日志。在LOG_INFO等宏的使用过程中,构造并析构匿名对象来完成日志信息的收集和输出。日志信息首先被存储到固定大小的缓冲区CFixedBuffer中,然后在CLogger析构时调用g_output将内容输出到标准设备。整个过程涉及到构造函数、重载操作符和匿名对象的生命周期管理。
摘要由CSDN通过智能技术生成

        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;

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值