muduo库的Logger及附属类剖析


我们先来看一下logger工作的流程:

它的宏是这样定义的(举出一例):

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

这代表我们使用该LOG_INFO宏时会先进行判断,如果级别大于INFO级别,后面那句不会被执行,也就是不会打印INFO级别的信息。

用法:LOG_INFO<<"info ...";    相当于使用muduo::Logger(__FILE__, __LINE__).stream()<<"Info";

Logger  --> impl  --> LogStream  --> operator<<  FilxedBuffer --> g_output --> g_flush

我们在使用LOG_INFO时,由于宏的替换,首先会构造一个无名临时Logger对象,然后调用该对象的stream()方法,该方法返回了在内部类impl中的成员缓冲区LogStream成员,这个成员针对所有类型重载了<<符号,我们写上“info ..."会调用该缓冲区的<<方法,把信息输入到该缓冲区中去。那么什么时候缓冲区会输出呢?

因为我们生成的无名临时对象,当无名临时对象析构的时候~Logger()中会调用g_output全局函数,该函数是一个回调函数,如果用户不主动更改,该函数默认会将缓冲区中的内容输出到stdout。以上就是Logger类基本的工作机制。


下面来看它们的类图:




下面试分析代码:

1.logger

class Logger
{
 public:
  enum LogLevel   //级别类型
  {
    TRACE,
    DEBUG,
    INFO,
    WARN,
    ERROR,
    FATAL,
    NUM_LOG_LEVELS,
  };

  // compile time calculation of basename of source file
  class SourceFile
  {
   public:
    template<int N>
    inline SourceFile(const char (&arr)[N])
      : data_(arr),
        size_(N-1)
    {
      const char* slash = strrchr(data_, '/'); // builtin function
      if (slash)
      {
        data_ = slash + 1;
        size_ -= static_cast<int>(data_ - arr);
      }
    }

    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(SourceFile file, int line);
  Logger(SourceFile file, int line, LogLevel level);
  Logger(SourceFile file, int line, LogLevel level, const char* func);
  Logger(SourceFile file, int line, bool toAbort);
  ~Logger();

  LogStream& stream() { return impl_.stream_; }

  static LogLevel logLevel();
  static void setLogLevel(LogLevel level);

  typedef void (*OutputFunc)(const char* msg, int len);
  typedef void (*FlushFunc)();
  static void setOutput(OutputFunc);
  static void setFlush(FlushFunc);
  static void setTimeZone(const TimeZone& tz);

 private:

class Impl    //实际上上logger类内部的一个嵌套类,封装了Logger的缓冲区stream_
{
 public:
  typedef Logger::LogLevel LogLevel;
  Impl(LogLevel level, int old_errno, const SourceFile& file, int line);  //级别错误文件行
  void formatTime();
  void finish();

  Timestamp time_;    //当前时间
  LogStream stream_;   //构造日志缓冲区,该缓冲区重载了各种<<,都是将数据格式到LogStream的内部成员缓冲区buffer里
  LogLevel level_;          //级别
  int line_;                   //行
  SourceFile basename_;    //基本名称
};

  Impl impl_;   //logger构造这个对象
  
};

extern Logger::LogLevel g_logLevel;

//返回当前日志级别
inline Logger::LogLevel Logger::logLevel()
{
  return g_logLevel;   
}

//
// CAUTION: do not write:
//
// if (good)
//   LOG_INFO << "Good news";
// else
//   LOG_WARN << "Bad news";
//
// this expends to
//
// if (good)
//   if (logging_INFO)
//     logInfoStream << "Good news";
//   else
//     logWarnStream << "Bad news";
//

//使用if条件判断,如果当前级别大于TRACE,就相当于没有下面一行代码,不会编译,下同。
//当客端调用这些宏时,相当于构造了一个无名临时Logger对象并且调用了它的stream()方法,然后该对象析构,
//我们可以去看看Logger的析构函数做了哪些事情,见上面
#define LOG_TRACE if (muduo::Logger::logLevel() <= muduo::Logger::TRACE) \
  muduo::Logger(__FILE__, __LINE__, muduo::Logger::TRACE, __func__).stream()
#define LOG_DEBUG if (muduo::Logger::logLevel() <= muduo::Logger::DEBUG) \
  muduo::Logger(__FILE__, __LINE__, muduo::Logger::DEBUG, __func__).stream()
#define LOG_INFO if (muduo::Logger::logLevel() <= muduo::Logger::INFO) \
  muduo::Logger(__FILE__, __LINE__).stream()
#define LOG_WARN muduo::Logger(__FILE__, __LINE__, muduo::Logger::WARN).stream()
#define LOG_ERROR muduo::Logger(__FILE__, __LINE__, muduo::Logger::ERROR).stream()
#define LOG_FATAL muduo::Logger(__FILE__, __LINE__, muduo::Logger::FATAL).stream()
#define LOG_SYSERR muduo::Logger(__FILE__, __LINE__, false).stream()
#define LOG_SYSFATAL muduo::Logger(__FILE__, __LINE__, true).stream()


.cpp:

#include <muduo/base/Logging.h>

#include <muduo/base/CurrentThread.h>
#include <muduo/base/Timestamp.h>
#include <muduo/base/TimeZone.h>

#include <errno.h>
#include <stdio.h>
#include <string.h>

#include <sstream>

namespace muduo
{

/*
class LoggerImpl
{
 public:
  typedef Logger::LogLevel LogLevel;
  LoggerImpl(LogLevel level, int old_errno, const char* file, int line);
  void finish();

  Timestamp time_;
  LogStream stream_;
  LogLevel level_;
  int line_;
  const char* fullname_;
  const char* basename_;
};
*/

__thread char t_errnobuf[512];
__thread char t_time[32];
__thread time_t t_lastSecond;

const char* strerror_tl(int savedErrno)
{
  return strerror_r(savedErrno, t_errnobuf, sizeof t_errnobuf);
}

Logger::LogLevel initLogLevel()    //初始化日志别别
{
  if (::getenv("MUDUO_LOG_TRACE"))    //获取TRACE环境变量,如果有,返回它
    return Logger::TRACE;
  else if (::getenv("MUDUO_LOG_DEBUG"))  //获取DEBUG环境变量,如果有,返回它
    return Logger::DEBUG;
  else
    return Logger::INFO;  //如果它们都没有,就使用INFO级别
}

Logger::LogLevel g_logLevel = initLogLevel();   //初始化日志级别

const char* LogLevelName[Logger::NUM_LOG_LEVELS] =
{
  "TRACE ",
  "DEBUG ",
  "INFO  ",
  "WARN  ",
  "ERROR ",
  "FATAL ",
};

// helper class for known string length at compile time
class T   //编译时获取字符串长度的类
{
 public:
  T(const char* str, unsigned len)
    :str_(str),
     len_(len)
  {
    assert(strlen(str) == len_);
  }

  const char* str_;
  const unsigned len_;
};

inline LogStream& operator<<(LogStream& s, T v)
{
  s.append(v.str_, v.len_);   //LogStream的重载,输出
  return s;
}

inline LogStream& operator<<(LogStream& s, const Logger::SourceFile& v)
{
  s.append(v.data_, v.size_);
  return s;
}

void defaultOutput(const char* msg, int len)  
{
  size_t n = fwrite(msg, 1, len, stdout);    //默认输出内容到stdout
  //FIXME check n
  (void)n;
}

void defaultFlush()   //默认刷新stdout
{
  fflush(stdout);
}

Logger::OutputFunc g_output = defaultOutput;   //默认输出方法
Logger::FlushFunc g_flush = defaultFlush;   //默认刷新方法
TimeZone g_logTimeZone;   

}

using namespace muduo;
                                                          //错误码,没有就传0
 Logger::Impl::Impl(LogLevel level, int savedErrno, const SourceFile& file, int line)
  : time_(Timestamp::now()),   //当前时间
    stream_(),      //初始化logger的四个成员
    level_(level),
    line_(line),
    basename_(file)
{
  formatTime();          //格式化时间,缓存当前线程id
  CurrentThread::tid();  //缓存当前线程id
  stream_ << T(CurrentThread::tidString(), CurrentThread::tidStringLength());   //格式化线程tid字符串
  stream_ << T(LogLevelName[level], 6);   //格式化级别,对应成字符串,先输出到缓冲区
  if (savedErrno != 0)
  {
    stream_ << strerror_tl(savedErrno) << " (errno=" << savedErrno << ") ";  //如果错误码不为0,还要输出相对应信息
  }
}

void Logger::Impl::formatTime()   //格式化时间
{
  int64_t microSecondsSinceEpoch = time_.microSecondsSinceEpoch();
  time_t seconds = static_cast<time_t>(microSecondsSinceEpoch / Timestamp::kMicroSecondsPerSecond);
  int microseconds = static_cast<int>(microSecondsSinceEpoch % Timestamp::kMicroSecondsPerSecond);
  if (seconds != t_lastSecond)
  {
    t_lastSecond = seconds;
    struct tm tm_time;
    if (g_logTimeZone.valid())
    {
      tm_time = g_logTimeZone.toLocalTime(seconds);
    }
    else
    {
      ::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;
  }

  if (g_logTimeZone.valid())
  {
    Fmt us(".%06d ", microseconds);   //格式化
    assert(us.length() == 8);
    stream_ << T(t_time, 17) << T(us.data(), 8);
  }
  else
  {
    Fmt us(".%06dZ ", microseconds);
    assert(us.length() == 9);
    stream_ << T(t_time, 17) << T(us.data(), 9);    //用stream进行输出,重载了<<
  }
}

void Logger::Impl::finish()   //首先将名字行输进缓冲区
{
  stream_ << " - " << basename_ << ':' << line_ << '\n';
}

Logger::Logger(SourceFile file, int line)
  : impl_(INFO, 0, file, line)
{
}

Logger::Logger(SourceFile file, int line, LogLevel level, const char* func)
  : impl_(level, 0, file, line)
{
  impl_.stream_ << func << ' ';   //格式化函数名称,上面的构造函数没有函数名称,不同的构造函数
}

Logger::Logger(SourceFile file, int line, LogLevel level) //同样格式化这三个参数
  : impl_(level, 0, file, line)
{
}

Logger::Logger(SourceFile file, int line, bool toAbort)  //是否终止
  : impl_(toAbort?FATAL:ERROR, errno, file, line)
{
}

//析构函数中会调用impl_的finish方法
Logger::~Logger()
{
  impl_.finish();   //将名字行数输入缓冲区
  const LogStream::Buffer& buf(stream().buffer());   //将缓冲区以引用方式获得
  g_output(buf.data(), buf.length());    //调用全部输出方法,输出缓冲区内容,默认是输出到stdout
  if (impl_.level_ == FATAL)
  {
    g_flush();
    abort();
  }
}

void Logger::setLogLevel(Logger::LogLevel level)  //设置日志级别
{
  g_logLevel = level;
}

void Logger::setOutput(OutputFunc out)  //设置输出函数,用来替代默认的
{
  g_output = out;
}

void Logger::setFlush(FlushFunc flush)  //用来配套你设置的输出函数的刷新方法
{
  g_flush = flush;
}

void Logger::setTimeZone(const TimeZone& tz)
{
  g_logTimeZone = tz;
}

2.LogStream

//缓冲区大小的配置
const int kSmallBuffer = 4000;
const int kLargeBuffer = 4000*1000;

template<int SIZE>
class FixedBuffer : boost::noncopyable
{
 public:
  FixedBuffer()
    : cur_(data_)
  {
    setCookie(cookieStart);  //设置cookie,muduo库这个函数目前还没加入功能,所以可以不用管
  }

  ~FixedBuffer()
  {
    setCookie(cookieEnd);
  }

  void append(const char* /*restrict*/ buf, size_t len)  //添加数据
  {
    // FIXME: append partially
    if (implicit_cast<size_t>(avail()) > len)  //如果可用数据足够,就拷贝过去,同时移动当前指针。
    {
      memcpy(cur_, buf, len);
      cur_ += len;
    }
  }

  const char* data() const { return data_; }  //返回首地址
  int length() const { return static_cast<int>(cur_ - data_); }   //返回缓冲区已有数据长度

  // write to data_ directly
  char* current() { return cur_; }  //返回当前数据末端地址
  int avail() const { return static_cast<int>(end() - cur_); }  //返回剩余可用地址
  void add(size_t len) { cur_ += len; }  //cur前移

  void reset() { cur_ = data_; }   //重置,不清数据,只需要让cur指回首地址即可
  void bzero() { ::bzero(data_, sizeof data_); }  //清零
  
  // for used by GDB
  const char* debugString();
  void setCookie(void (*cookie)()) { cookie_ = cookie; }
  // for used by unit test
  string toString() const { return string(data_, length()); }
  StringPiece toStringPiece() const { return StringPiece(data_, length()); }  //返回string类型

 private:
  const char* end() const { return data_ + sizeof data_; } //返回尾指针
  // Must be outline function for cookies.
  static void cookieStart();
  static void cookieEnd();

  void (*cookie_)();
  char data_[SIZE];   //缓冲区数组
  char* cur_;      //cur永远指向已有数据的最右端,data->cur->end结构?
};

}

class LogStream : boost::noncopyable
{
  typedef LogStream self;
 public:
  typedef detail::FixedBuffer<detail::kSmallBuffer> Buffer;  //缓冲区,使用smallbuffer

  //针对不同类型重载了operator<<
  self& operator<<(bool v)   //别的类如果调用LogStream的<<实际上是把内容追加到LogStream的缓冲区。
  {
    buffer_.append(v ? "1" : "0", 1);  //追加
    return *this;
  }
  self& operator<<(short);
  self& operator<<(unsigned short);
  self& operator<<(int);
  self& operator<<(unsigned int);
  self& operator<<(long);
  self& operator<<(unsigned long);
  self& operator<<(long long);
  self& operator<<(unsigned long long);

  self& operator<<(const void*);

  self& operator<<(float v)
  {
    *this << static_cast<double>(v);    //把float类型转化为double类型,调用下面的重载函数
    return *this;
  }
  self& operator<<(double);
  // self& operator<<(long double);

  self& operator<<(char v)
  {
    buffer_.append(&v, 1);
    return *this;
  }

  // self& operator<<(signed char);
  // self& operator<<(unsigned char);

  self& operator<<(const char* str)
  {
    if (str)
    {
      buffer_.append(str, strlen(str));
    }
    else
    {
      buffer_.append("(null)", 6);
    }
    return *this;
  }

  self& operator<<(const unsigned char* str)
  {
    return operator<<(reinterpret_cast<const char*>(str));
  }

  self& operator<<(const string& v)
  {
    buffer_.append(v.c_str(), v.size());
    return *this;
  }

#ifndef MUDUO_STD_STRING
  self& operator<<(const std::string& v)
  {
    buffer_.append(v.c_str(), v.size());
    return *this;
  }
#endif

  self& operator<<(const StringPiece& v)
  {
    buffer_.append(v.data(), v.size());
    return *this;
  }

  self& operator<<(const Buffer& v)
  {
    *this << v.toStringPiece();
    return *this;
  }

  void append(const char* data, int len) { buffer_.append(data, len); }
  const Buffer& buffer() const { return buffer_; }
  void resetBuffer() { buffer_.reset(); }

 private:
  void staticCheck();

  template<typename T>
  void formatInteger(T);

  Buffer buffer_;

  static const int kMaxNumericSize = 32;
};

class Fmt // : boost::noncopyable
{
 public:
  template<typename T>
  Fmt(const char* fmt, T val);   //把整数按照T类型格式化到buffer中

  const char* data() const { return buf_; }
  int length() const { return length_; }  
 private:
  char buf_[32];
  int length_;
};

inline LogStream& operator<<(LogStream& s, const Fmt& fmt)
{
  s.append(fmt.data(), fmt.length());
  return s;
}

}
#endif  // MUDUO_BASE_LOGSTREAM_H

.cpp

const char digits[] = "9876543210123456789";
const char* zero = digits + 9;  //上面的数组偏移第9位,值为0
BOOST_STATIC_ASSERT(sizeof(digits) == 20);

const char digitsHex[] = "0123456789ABCDEF";
BOOST_STATIC_ASSERT(sizeof digitsHex == 17);

// Efficient Integer to String Conversions, by Matthew Wilson.
//这个函数实现将一个整数转换成字符串
template<typename T>
size_t convert(char buf[], T value)
{
  T i = value;
  char* p = buf;

  do
  {//lsd意思是last digit,最后一位数字
    int lsd = static_cast<int>(i % 10);//最后一位,如输入123,第一次就取3
    i /= 10;
    *p++ = zero[lsd];//zero指针指向0,然后偏移lsd个位置,见上面,由于上面digits是字符串,索引加偏移得到的就是字符,相当于数字转化成了字符,3->'3'
									//同时由于p指向buf,p++,那么会得到一个逆转的字符串,321
  } while (i != 0);

  if (value < 0)  //如果小于0,加个负号
  {
    *p++ = '-';
  }
  *p = '\0';  //加上\0
  std::reverse(buf, p);   //由于上面得到的是逆序,所以reverse一下

  return p - buf;   //返回长度
}

size_t convertHex(char buf[], uintptr_t value)   //与上面类似
{
  uintptr_t i = value;
  char* p = buf;

  do
  {
    int lsd = static_cast<int>(i % 16);
    i /= 16;
    *p++ = digitsHex[lsd];
  } while (i != 0);

  *p = '\0';
  std::reverse(buf, p);

  return p - buf;
}

template class FixedBuffer<kSmallBuffer>;
template class FixedBuffer<kLargeBuffer>;

}
}

template<int SIZE>   //使用非类型模板参数
const char* FixedBuffer<SIZE>::debugString()
{
  *cur_ = '\0';   //加个\0把缓冲区作为字符串
  return data_;
}

template<int SIZE>
void FixedBuffer<SIZE>::cookieStart()
{
}

template<int SIZE>
void FixedBuffer<SIZE>::cookieEnd()
{
}

void LogStream::staticCheck()
{
  BOOST_STATIC_ASSERT(kMaxNumericSize - 10 > std::numeric_limits<double>::digits10);
  BOOST_STATIC_ASSERT(kMaxNumericSize - 10 > std::numeric_limits<long double>::digits10);
  BOOST_STATIC_ASSERT(kMaxNumericSize - 10 > std::numeric_limits<long>::digits10);
  BOOST_STATIC_ASSERT(kMaxNumericSize - 10 > std::numeric_limits<long long>::digits10);
}

template<typename T>
void LogStream::formatInteger(T v)
{
  if (buffer_.avail() >= kMaxNumericSize)
  {
    size_t len = convert(buffer_.current(), v);  //通过调用convert把整数转化成字符串
    buffer_.add(len);
  }
}

//下面这些<<最终都要调用formatIneteger()将字符串转换成整数。
LogStream& LogStream::operator<<(short v)
{
  *this << static_cast<int>(v);
  return *this;
}

LogStream& LogStream::operator<<(unsigned short v)
{
  *this << static_cast<unsigned int>(v);
  return *this;
}

LogStream& LogStream::operator<<(int v)
{
  formatInteger(v);
  return *this;
}

LogStream& LogStream::operator<<(unsigned int v)
{
  formatInteger(v);
  return *this;
}

LogStream& LogStream::operator<<(long v)
{
  formatInteger(v);
  return *this;
}

LogStream& LogStream::operator<<(unsigned long v)
{
  formatInteger(v);
  return *this;
}

LogStream& LogStream::operator<<(long long v)
{
  formatInteger(v);
  return *this;
}

LogStream& LogStream::operator<<(unsigned long long v)
{
  formatInteger(v);
  return *this;
}

//和转化fomatInteger类似
LogStream& LogStream::operator<<(const void* p)   //输入地址
{
  uintptr_t v = reinterpret_cast<uintptr_t>(p);
  if (buffer_.avail() >= kMaxNumericSize)  //如果够用
  {
    char* buf = buffer_.current();
    buf[0] = '0';
    buf[1] = 'x';
    size_t len = convertHex(buf+2, v);
    buffer_.add(len+2);
  }
  return *this;
}

// FIXME: replace this with Grisu3 by Florian Loitsch.
LogStream& LogStream::operator<<(double v)
{
  if (buffer_.avail() >= kMaxNumericSize)
  {
    int len = snprintf(buffer_.current(), kMaxNumericSize, "%.12g", v);
    buffer_.add(len);
  }
  return *this;
}

template<typename T>
Fmt::Fmt(const char* fmt, T val)
{
	//断言是算术类型
  BOOST_STATIC_ASSERT(boost::is_arithmetic<T>::value == true);
	//格式化到缓冲区中
  length_ = snprintf(buf_, sizeof buf_, fmt, val);
  assert(static_cast<size_t>(length_) < sizeof buf_);
}

// Explicit instantiations
//模板的特化,只支持这么多类型
template Fmt::Fmt(const char* fmt, char);

template Fmt::Fmt(const char* fmt, short);
template Fmt::Fmt(const char* fmt, unsigned short);
template Fmt::Fmt(const char* fmt, int);
template Fmt::Fmt(const char* fmt, unsigned int);
template Fmt::Fmt(const char* fmt, long);
template Fmt::Fmt(const char* fmt, unsigned long);
template Fmt::Fmt(const char* fmt, long long);
template Fmt::Fmt(const char* fmt, unsigned long long);

template Fmt::Fmt(const char* fmt, float);
template Fmt::Fmt(const char* fmt, double);





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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值