muduo基础库学习--日志

日志级别

  • TRACE:比DEBUG更详细(开发过程中使用)
  • DEBUG:较详细(开发过程中使用)
  • INFO:粗粒度,突出运行过程
  • WARN:警告。可运行,但可能有潜在错误
  • ERROR:发生错误,但不影响系统继续运行
  • FATAL:严重错误,导致应用程序退出

日志级别可更改,以适应不同阶段开发。

//Logging.h

#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()

//Logging.h

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>
    SourceFile(const char (&arr)[N])
      : data_(arr),
        size_(N-1)
    {
      ...
    }

    explicit SourceFile(const char* filename)
      : data_(filename)
    {
      ...
    }
  };

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

 private:

class Impl
{
	...
	LogStream stream_;
};

  Impl impl_;

};

//LogStream.h

class LogStream : noncopyable
{
 public:
 //存放日志的缓冲区
  typedef detail::FixedBuffer<detail::kSmallBuffer> Buffer;
  ...
}
  • Logger : 日志类
  • Impl:实际实现的类(日志信息)
  • LogStream : 流的封装,存放缓冲区,重载 << 符号

运行流程:
Logger(Impl) --> LogStream --> 重载<<输出到缓冲区FixedBuffer --> g_output(输出到设备、标准输出、文件等) --> g_flush

//Logging.cc

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::Impl::Impl(LogLevel level, int savedErrno, const SourceFile& file, int line)
  : time_(Timestamp::now()),
    stream_(),
    level_(level),
    line_(line),
    basename_(file)
{
...
  stream_ << T(CurrentThread::tidString(), CurrentThread::tidStringLength());
  stream_ << T(LogLevelName[level], 6);
...
}

Logger::~Logger()
{
  impl_.finish();
  //引用缓冲区,避免拷贝内存
  const LogStream::Buffer& buf(stream().buffer());
 //将缓冲区内容输出到设备或其他
  g_output(buf.data(), buf.length());
  if (impl_.level_ == FATAL)
  {
    g_flush();
    abort();
  }
}

(1)日志使用方法:语句“LOG_*** << str;” 在宏定义中,直接构造了匿名 Logger对象,并调用stream();

(2)在Logger类的构造函数中,会构造Impl对象,调用Impl构造函数,在其 中将日志部分信息(时间,tid等)写入缓冲;

(3)当LOG_**语句执行后,返回LogStream对象,LogStream重载了<<符号,所以将str格式化写入缓冲;

(4)语句结束时,调用Logger析构函数,由缓冲区数据写入设备或标准输出。

//Logging.cc

//g_output(buf.data(), buf.length());析构函数中执行

//Logging.h
//定义了一个函数指针
typedef void (*OutputFunc)(const char* msg, int len);

//默认为标准输出
Logger::OutputFunc g_output = defaultOutput;
void defaultOutput(const char* msg, int len)
{
  size_t n = fwrite(msg, 1, len, stdout);
  //FIXME check n
  (void)n;
}
  • 缓冲区类

//LogStream.h

template<int SIZE>
class FixedBuffer : noncopyable
{
 public:
  FixedBuffer() : cur_(data_) {}
  ~FixedBuffer(){}

  void append(const char* /*restrict*/ buf, size_t len)
  {
    //implicit_cast<ToType>(expr)
    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; }

  void reset() { cur_ = data_; }
  //void* memset(void* s, int ch, size_t n):将s中当前位置后面的n个字节用ch替换并返回s 
  void bzero() { memset(data_, 0, sizeof data_); }

  // for used by GDB
  const char* debugString(){ //将data_作为字符串处理。字符数组和字符串的区别
  *cur_ = '\0';
  return data_;
  };
	...

 private:
  //end() 指向字符数组末尾的下一位置(并非数据末尾)
  const char* end() const { return data_ + sizeof data_; }
	...
  char data_[SIZE];
  char* cur_;
};

data_指向首字节,cur_指向末尾数据的下一字节

  • LogStream类
class LogStream : noncopyable
{
  typedef LogStream self;
 public:
  typedef detail::FixedBuffer<detail::kSmallBuffer> Buffer;

  self& operator<<(bool v)
  {
    buffer_.append(v ? "1" : "0", 1);
    return *this;
  }

  self& operator<<(short);
  self& operator<<(unsigned short);
  self& operator<<(int);
  ...
  self& operator<<(const void*);

  self& operator<<(float v)
  {
    *this << static_cast<double>(v);
    return *this;
  }
  self& operator<<(double);

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

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

  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:
  template<typename T>
  void formatInteger(T);

  Buffer buffer_;

  static const int kMaxNumericSize = 32;
}

//LogStream.cc

  • 将整数放入缓冲区(为什么不直接用snprintf?)
template<typename T>
void LogStream::formatInteger(T v)
{
  if (buffer_.avail() >= kMaxNumericSize)
  {
    size_t len = convert(buffer_.current(), v);
    buffer_.add(len);
  }
}

//将整数转化为字符(字节)数组的函数,以便放入缓冲区
const char digits[] = "9876543210123456789";
const char* zero = digits + 9; //zero指向'0'

template<typename T>
size_t convert(char buf[], T value)
{
  T i = value;
  char* p = buf;

  do //将数字转换为字符
  {
    int lsd = static_cast<int>(i % 10);//取个位数
    i /= 10;//i去掉个位数
    *p++ = zero[lsd];//detail::zero,索引是几就偏移几位
  } while (i != 0);

  if (value < 0)
  {
    *p++ = '-';
  }
  *p = '\0';
  //例:“-123”,存进去为“321-”,需反转
  std::reverse(buf, p); //接受参数:begin(),end()(不包含结束指针指向字符)

  return p - buf;
}
  • Fmt类:将T类型格式化存储(应对不是直接存储数据的需求)

//LogStream.h

class Fmt // : noncopyable
{
 public:
  template<typename T>
  Fmt(const char* fmt, T val);

  const char* data() const { return buf_; }
  int length() const { return length_; }

 private:
  char buf_[32];
  int length_;
};

template<typename T>
Fmt::Fmt(const char* fmt, T val)
{
//断言T是算数类型
  static_assert(std::is_arithmetic<T>::value == true, "Must be arithmetic type"); 
  length_ = snprintf(buf_, sizeof buf_, fmt, val);
  assert(static_cast<size_t>(length_) < sizeof buf_);
}

template Fmt::Fmt(const char* fmt, char);
template Fmt::Fmt(const char* fmt, int);
...

inline LogStream& operator<<(LogStream& s, const Fmt& fmt)
{
  s.append(fmt.data(), fmt.length());
  return s;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值