欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

muduo的日志库分析二之Logger类

程序员文章站 2022-06-14 10:18:43
...

github地址:

https://github.com/chenshuo/muduo/blob/master/muduo/base/Logging.h

https://github.com/chenshuo/muduo/blob/master/muduo/base/Logging.cc

Logger类图

muduo的日志库分析二之Logger类
muduo的日志库分析二之Logger类
Logger使用时序图如下:
muduo的日志库分析二之Logger类

#define LOG_INFO if (muduo::Logger::logLevel() <= muduo::Logger::INFO) \
  muduo::Logger(__FILE__, __LINE__).stream()

LOG_INFO<<“info ...”; // 使用方式
现在来说一下Logger类和LogStream类是怎么配合工作的。
      使用LOG_*之类的宏会创建一个临时匿名Logger对象,这个对象有一个Impl对象,而Impl对象有一个LogStream对象。LOG_*宏会返回一个LogStream对象的引用。用于将内容输入到LogStream中的一个buffer中。在Logger的析构函数中,调用 g_output,即 g_output(buf.data(), buf.length()),将存于LogStream的buffer的日志内容输出。如果是FATAL错误,还要调用g_flush,最后abort()程序。如果没有调用g_flush,会一直输出到缓冲区(标准输出缓冲区,文件FILE缓冲区)满才会真的输出在标准输出,或者写入到文件中去。

Logger::~Logger()
{
  impl_.finish();
  const LogStream::Buffer& buf(stream().buffer());  // 获取缓冲区
  g_output(buf.data(), buf.length());  // 默认输出到stdout

  // 当日志级别为FATAL时,flush设备缓冲区并终止程序
  if (impl_.level_ == FATAL) 
  {
    g_flush();
    abort();
  }
}
    注:可以使用setvbuf设置缓冲区的大小。

 int setvbuf ( FILE * stream, char * buffer, int mode, size_t size );
   默认日志信息输出到标准输出(g_output = defaultOutput、g_flush = defaultFlush),也可以输出到文件,使用SetOutput、SetFlush 设置。
        LOG_*的宏,创建一个临时匿名Logger对象,临时匿名很重要,因为临时匿名对象是一使用完就马上销毁,调用析构函数。而C++对于栈中的具名对象,先创建的后销毁。这就使得后创建的Logger对象先于先创建的Logger对象销毁。即先调用析构函数将日志输出,这就会使得日志内容反序(具体说是一个由{}包括的块中反序)。使用临时匿名Logger对象的效果就是:LOG_*这行代码不仅仅包含日志内容,还会马上把日志输出。

      LOG_*之类的宏,先构造临时匿名Logger对象,muduo::Logger(__FILE__, __LINE__),其中__FILE__返回的是文件所在的绝对路径,Logger的嵌套类SourceFile,SourceFile类是用来截取绝对路径最后面的文件名。

//__FILE__ 日志记录的信息原文件  
class SourceFile  
{  
    public:  
    template<int N>   
    inline SourceFile(const char (&arr)[N])  
    : data_(arr),  
    size_(N-1)  
    {  
        const char* slash = strrchr(data_, '/');//返回最后一个 '/'  
        //截取的文件名  
        if(slash)  
        {  
            data_ = slash + 1;  
            size_ = static_cast<int>(data_ - arr ); 
        }  
    }  
    // muduo::Logger(__FILE__, __LINE__).stream() 调用第二个构造函数char *的  
    explicit SourceFile(const char* filename)  
        : data_(filename)  
        {  
            const char* slash = strrchr(filename, '/');  
            if(slash)  
                data_ = slash + 1;  
            size_ = static_cast<int>(strlen(data_));  
        }  
    const char* data_;  
    int size_;  
};  

 Logger类内部定义了一个私有的Impl类。这个Impl类有一个LogStream类。同大多数Impl方法一样,Logger类的具体操作由这个Impl类来完成。不过,Logger类中的Impl成员不是一个指针。使用Impl方法的一大原因是为了闭源,不仅仅隐藏实现代码还隐藏类的私有成员函数。但对于muduo这个开源库来说,这没有意义。而且使用指针的话,new的时候需要在堆中申请空间,这无疑会降低运行速度。

//Loger类的内部嵌套类
//Impl类主要是负责日志的格式化
class Impl
{
 public:
  typedef Logger::LogLevel LogLevel;
  //构造函数
  Impl(LogLevel level, int old_errno, const SourceFile& file, int line);
  //格式化时间函数
  void formatTime();
  void finish();

  Timestamp time_;//Timestamp时间戳
  LogStream stream_;//LogStream类对象成员
  LogLevel level_;//日志级别
  int line_;//行号
  SourceFile basename_;
};
//Impl类的构造函数
//级别,错误(没有错误则传0),文件,行
//Impl类主要是负责日志的格式化
Logger::Impl::Impl(LogLevel level, int savedErrno, const SourceFile& file, int line)
  : time_(Timestamp::now()),//登记当前的时间
    stream_(),//LogStream类
    level_(level),//级别
    line_(line),//行
    basename_(file)//文件名称
{
  formatTime();//格式化时间
  CurrentThread::tid();//缓存当前线程的id
  //输出字符串格式的线程id
  stream_ << T(CurrentThread::tidString(), 6);
  stream_ << T(LogLevelName[level], 6);
  if (savedErrno != 0)//如果savedErrno不为零,还要输出对应的信息
  {
    stream_ << strerror_tl(savedErrno) << " (errno=" << savedErrno << ") ";
  }
}

void Logger::Impl::formatTime()
{
  int64_t microSecondsSinceEpoch = time_.microSecondsSinceEpoch();//获得微秒格式的时间
  time_t seconds = static_cast<time_t>(microSecondsSinceEpoch / 1000000);//获得秒
  int microseconds = static_cast<int>(microSecondsSinceEpoch % 1000000);//微秒
  if (seconds != t_lastSecond)
  {
    t_lastSecond = seconds;
    struct tm tm_time;
    ::gmtime_r(&seconds, &tm_time); // FIXME TimeZone::fromUtcTime

    int len = snprintf(t_time, sizeof(t_time), "%4d%02d%02d %02d:%02d:%02d",
        tm_time.tm_year + 1900, tm_time.tm_mon + 1, tm_time.tm_mday,
        tm_time.tm_hour, tm_time.tm_min, tm_time.tm_sec);
    assert(len == 17); (void)len;
  }
  Fmt us(".%06dZ ", microseconds);
  assert(us.length() == 9);
  //LogStream类重载运算符,把信息输出到缓冲区
  stream_ << T(t_time, 17) << T(us.data(), 9);
}







相关标签: muduo C 日志