muduo库的高性能日志库(三)——Logging文件

接下看一下Logging文件,该文件主要负责全局日志级别,输出目的地设置
Logger内有两个内部类
SourceFile,主要负责获取文件名
Impl,实际用于日志消息的处理类

SourceFile内部类

该类就负责获取基文件名
eg:
/home/gty/muduo/muduo/base/test.cc
对应的基文件名就是
test.cc

实现很简单,调用了c语言库函数strrchr

C 库函数char *strrchr(const char *str, int c)
在参数 str 所指向的字符串中搜索最后一次出现字符 c(一个无符号字符)的位置

  class SourceFile
  {
    //找到基文件的文件名,并将data_指向基文件名的开始,size_存文件名长度
   public:
    template<int N>
    SourceFile(const char (&arr)[N])  //数组引用
      : data_(arr),
        size_(N-1)
    {
      //C 库函数 char *strrchr(const char *str, int c) 
      //在参数 str 所指向的字符串中搜索最后一次出现字符 c(一个无符号字符)的位置

      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_;
  };

Impl内部类

该类就是指定了一条日志的具现化

|----日志打印时间
|----日志缓存(LogStream)
|----日志的level
|----行号
|----文件名称

具体如下

//内部类,实际用于日志消息的处理类
class Impl
{
 public:
  typedef Logger::LogLevel LogLevel;
  Impl(LogLevel level, int old_errno, const SourceFile& file, int line);
  //格式化时间戳
  void formatTime();
  void finish();
  Timestamp time_;   //日志时间戳
  LogStream stream_; //日志缓存流
  LogLevel level_;   //日志级别
  int line_;         //当前记录日志宏的源代码名称
  SourceFile basename_;  //当前记录日志宏的源代码名称
};

下面看看该类的实现细节

内部实现细节

__thread char t_errnobuf[512];
__thread char t_time[64];  //当前线程的时间字符串“年:月:日:时:分:秒”
__thread time_t t_lastSecond;  //当前线程上一次日志记录时的秒数

//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(); //缓存当前线程tid

  //将当前线程id和日志等级名称输出到buf当中
  stream_ << T(CurrentThread::tidString(), CurrentThread::tidStringLength());
  stream_ << T(LogLevelName[level], 6);
  if (savedErrno != 0)  //如果savedErrno不为0,saveErrno为错误号
  {
    //将错误码对应的错误信息存入t_errnobuf当中,并且输入到stream_的buf当中
    stream_ << strerror_tl(savedErrno) << " (errno=" << savedErrno << ") ";
  }
}


//格式化时间
void Logger::Impl::formatTime()
{
  int64_t microSecondsSinceEpoch = time_.microSecondsSinceEpoch();//获取微秒格式时间
  //获得秒,kMicroSecondsPerSecond为1000000
  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);
  }
}


//所有信息输出完毕,然后再将文件名称和行号输出到buf当中
void Logger::Impl::finish()
{
  stream_ << " - " << basename_ << ':' << line_ << '\n';
}

Logger类

Logger的两个内部类看完,分析这个类

1. 规定日志的几个等级

    |----TRACE
    |----DEBUG
    |----INFO
    |----WARN
    |----ERROR
    |----FATAL
    |----NUM_LOG_LEVELS

代码如下:

  enum LogLevel
  {
    TRACE,
    DEBUG,
    INFO,
    WARN,
    ERROR,
    FATAL,
    NUM_LOG_LEVELS,
  };

2. 两个内部类

|----SourceFile  (文件名截取)
|----Impl        (实际日志信息处理类)

3. 设置日志属性

|----setLogLevel(***)  //设置日志等级
|----setOutput(***)    //设置输出函数
|----setFlush(***)     //设置刷新函数
|----setTimeZone(***)  //设置日期格式

代码如下:

  //设置日志级别
  static void setLogLevel(LogLevel level);
  //设置输出函数(默认的输出函数是输出到stdout)
  typedef void (*OutputFunc)(const char* msg, int len);
  //设置刷新函数(默认的刷新函数是刷新到stdout)
  typedef void (*FlushFunc)();
  static void setOutput(OutputFunc);  //默认fwrite到stdout
  static void setFlush(FlushFunc);    //默认fflush到stdout
  static void setTimeZone(const TimeZone& tz);  默认 GMT

下面为默认的OutputFuncFlushFunc

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

//默认为stdout
void defaultFlush()
{
  fflush(stdout);
}
//设置默认的输出和刷新函数
Logger::OutputFunc g_output = defaultOutput;
Logger::FlushFunc g_flush = defaultFlush;
TimeZone g_logTimeZone;

初始化日志等级

//初始化日至等级
Logger::LogLevel initLogLevel()
{

  //getenv()用来取得参数name环境变量的内容。
  //参数name为环境变量的名称,如果该变量存在则会返回指向该内容的指针。
  if (::getenv("MUDUO_LOG_TRACE"))
    return Logger::TRACE;
  else if (::getenv("MUDUO_LOG_DEBUG"))
    return Logger::DEBUG;
  else
    return Logger::INFO;
}

Logger::LogLevel g_logLevel = initLogLevel();

4. 构造函数

有四个重载
四种不同类型的日志格式

  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);

5. 析构函数

日志打印的基本所有操作都是在析构中进行的

//主要在析构进行
Logger::~Logger()
{
  impl_.finish();
  const LogStream::Buffer& buf(stream().buffer());
  g_output(buf.data(), buf.length());
  if (impl_.level_ == FATAL)
  {
    //如果这是一个FATAL刷新缓冲区,然后直接中断程序
    g_flush();
    abort();
  }
}

日志宏

使用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缓冲区)满才会真的输出在标准输出,或者写入到文件中去。

//日志宏
#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()
  • 3
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

binary~

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值