muduo::Logging、LogStream分析

  • Logging
  • LogStream

Logging

Logging类是用来记录分析日志用的。

下面是Logging.h的源码

namespace muduo
{

class TimeZone;//forward declaration

class Logger
{
 public:
  enum LogLevel//用来设置不同的日志级别
  {
    TRACE,
    DEBUG,
    INFO,
    WARN,
    ERROR,
    FATAL,
    NUM_LOG_LEVELS,//级别个数
  };

  // compile time calculation of basename of source file
  class SourceFile// help to get the name of a file.
  {
   public:
    template<int N>
    inline SourceFile(const char (&arr)[N])
      : data_(arr),
        size_(N-1)
    {
      const char* slash = strrchr(data_, ‘/‘); // builtin function. Find the last  ‘\‘ in a string and return it‘s position
      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_;//point to the fie‘s name
    int size_;//file‘s name‘s length
  };

  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_; }//return he reference of a LogStream Object。LogStream重载了<<运算符,因此可以直接使用

  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//这里是真正的实现
{
 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对象
  LogLevel level_;
  int line_;//行号
  SourceFile basename_;
};

  Impl impl_;//实现对象

};

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";
//使用宏来定义匿名对象,LogStream重载了<<,因此可以使用 LOG_REACE<<"日志"<<
#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()

const char* strerror_tl(int savedErrno);

// Taken from glog/logging.h
//
// Check that the input is non NULL.  This very useful in constructor
// initializer lists.

#define CHECK_NOTNULL(val) \
  ::muduo::CheckNotNull(__FILE__, __LINE__, "‘" #val "‘ Must be non NULL", (val))

// A small helper for CHECK_NOTNULL().
template <typename T>
T* CheckNotNull(Logger::SourceFile file, int line, const char *names, T* ptr)
{
  if (ptr == NULL)
  {
   Logger(file, line, Logger::FATAL).stream() << names;
  }
  return ptr;
}

}

在Logger类中有2个内部类SourceFile和Impl。类SouceFile用来确定日志文件的名字,而Impl是真正实现日志输出的地方。在Logger类中可以设置Logger日志级别,以及设置缓存和清空缓存函数。

在文件最后使用宏定义定了了LOG_*为Logger的匿名对象返回的LogStream对象,最后使用匿名对象来输出日志,匿名对象只存在构造这个匿名对象的那行代码,离开这行代码马上析构,在析构时会清空缓存,输出日志。

Logger.cc如下

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变量,和线程存储相关
__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"))
    return Logger::TRACE;
  else if (::getenv("MUDUO_LOG_DEBUG"))
    return Logger::DEBUG;
  else
    return Logger::INFO;
}

Logger::LogLevel g_logLevel = initLogLevel();
//日志级别的string形式,方便输出到缓存
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_);
  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()
{
  fflush(stdout);//默认输出到stdout
}

Logger::OutputFunc g_output = defaultOutput;
Logger::FlushFunc g_flush = defaultFlush;
TimeZone g_logTimeZone;

}

using namespace muduo;
//Impl构造函数
Logger::Impl::Impl(LogLevel level, int savedErrno, const SourceFile& file, int line)
  : time_(Timestamp::now()),
    stream_(),
    level_(level),
    line_(line),
    basename_(file)
{//日志格式:时间---线程ID---日志级别
  formatTime();//时间
  CurrentThread::tid();//线程ID
  stream_ << T(CurrentThread::tidString(), CurrentThread::tidStringLength());
  stream_ << T(LogLevelName[level], 6);
  if (savedErrno != 0)
  {
    stream_ << strerror_tl(savedErrno) << " (errno=" << savedErrno << ") ";
  }
}

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

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)
{
}

Logger::~Logger()//在析构函数输出缓存
{
  impl_.finish();//给buffer缓冲去添加‘/0‘
  const LogStream::Buffer& buf(stream().buffer());//返回buffer
  g_output(buf.data(), buf.length());
  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;
}

LogStream

LogStream.h定义了Buffer类和LogStream类。Buffer类是缓存,日志先缓存到Buffer中,之后再输出。LogStream类包含buffer,主要实现各种类型数据到字符串的转换,并把数据添加到buffer中。添加时使用append()函数,如果日志太长,将无法添加,但是每条日志输出都是构建了一个匿名类,不存在日志叠加到一个缓存的问题。

LogStream.cc源码

namespace muduo
{
namespace detail
{

const char digits[] = "9876543210123456789";
const char* zero = digits + 9;//zero两边对称,因为余数可能为负数
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)//把value转为字符串,并返回字符串放到buf中,并返回字符串长度
{
  T i = value;
  char* p = buf;

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

  if (value < 0)
  {
    *p++ = ‘-‘;
  }
  *p = ‘\0‘;
  std::reverse(buf, p);//逆转[buf p)

  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‘;
  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)//将T类型v转为10进制并缀到buffer_后
{
  if (buffer_.avail() >= kMaxNumericSize)
  {
    size_t len = convert(buffer_.current(), v);
    buffer_.add(len);
  }
}
//重载<<的实现
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;
}

LogStream& LogStream::operator<<(const void* p)//将p指向的内容转换为十六进制并缀到buffer_
{
  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);

日志的输时,调用顺序如下

Logger -> Impl -> LogStream -> operator<< -> FixedBuffer -> g_output -> g_flush

可以写个测试,测试log日志的输出,默认输出日志到stdout。

#include <muduo/base/Logging.h>
#include <muduo/base/Thread.h>

#include <boost/bind.hpp>

#include <errno.h>

using namespace muduo;

void thread1()
{
    LOG_TRACE << "Thread 1. TRACE  LEVEL";
    LOG_DEBUG << "Thread 1 . DEBUG LEVEL";
    LOG_INFO << "Thread 1. INFO LEVEL";
    LOG_WARN << "Thread 1. WARN LEVEL";
    LOG_ERROR << "Thread 1. ERROR LEVEL";
    errno = 1;
    LOG_SYSERR << "Thread 1. SYSERR LEVEL";

}
void thread2()
{
    LOG_TRACE << "Thread 2. TRACE  LEVEL";
    LOG_DEBUG << "Thread 2 . DEBUG LEVEL";
    LOG_INFO << "Thread 2. INFO LEVEL";
    LOG_WARN << "Thread 2. WARN LEVEL";
    LOG_ERROR << "Thread 2. ERROR LEVEL";
    errno = 2;
    LOG_SYSERR << "Thread 1. SYSERR LEVEL";

}
int main()
{
    Thread t1(boost::bind(thread1), "thread 1");
    Thread t2(boost::bind(thread2), "thread 2");
    t1.start();
    t2.start();

    t1.join();
    t2.join();

    return 0;
}

版权声明:本文为博主原创文章,未经博主允许不得转载。

时间: 2024-11-05 08:53:22

muduo::Logging、LogStream分析的相关文章

muduo源码分析--我对muduo的理解

分为几个模块 EventLoop.TcpServer.Acceptor.TcpConnection.Channel等 对于EventLoop来说: 他只关注里面的主驱动力,EventLoop中只关注poll,这类系统调用使得其成为Reactor模式,EventLoop中有属于这个loop的所有Channel,这个loop属于哪一个Server. 几个类存在的意义: 从应用层使用的角度来看,用户需要初始化一个EventLoop,然后初始化一个TcpServer(当然也可以自定义个TcpServer

muduo::thread类分析

在看源代码前,先学习一个关键字:__thread. 线程共享进程的数据,如果想要每个线程都有一份独立的数据,那么可以使用__thread关键字修饰数据. __thread只能用于修饰POD类型的数据,不能修饰class,因为它无法调用构造函数和析构函数.__thread可以修饰全局变量.函数内的静态变量,不能修饰函数内的局部变量或class的普通成员变量. 在muduo/base/thread.{h, cc}中实现了线程的封装.thread的封装和一个命名空间muduo::CurrentThre

muduo源码分析--Reactor模式在muduo中的使用

一. Reactor模式简介 Reactor释义"反应堆",是一种事件驱动机制.和普通函数调用的不同之处在于:应用程序不是主动的调用某个API完成处理,而是恰恰相反,Reactor逆置了事件处理流程,应用程序需要提供相应的接口并注册到Reactor上,如果相应的时间发生,Reactor将主动调用应用程序注册的接口,这些接口又称为"回调函数". 二. moduo库Reactor模式的实现 muduo主要通过3个类来实现Reactor模式:EventLoop,Chann

muduo源码分析--Reactor模式的在muduo中的使用(二)

一. TcpServer类: 管理所有的TCP客户连接,TcpServer供用户直接使用,生命期由用户直接控制.用户只需设置好相应的回调函数(如消息处理messageCallback)然后TcpServer::start()即可. 主要数据成员: boost::scoped_ptr<Accepter> acceptor_; 用来接受连接 std::map<string,TcpConnectionPtr> connections_; 用来存储所有连接 connectonCallbac

C++ stringstream介绍,使用方法与例子

From: http://www.usidcbbs.com/read-htm-tid-1898.html C++引入了ostringstream.istringstream.stringstream这三个类,要使用他们创建对象就必须包含sstream.h头文件. istringstream类用于执行C++风格的串流的输入操作. ostringstream类用于执行C风格的串流的输出操作. strstream类同时可以支持C风格的串流的输入输出操作. istringstream类是从istream

WEB安全番外第六篇--关于通过记录渗透工具的Payload来总结和学习测试用例

背景: 在WEB安全的学习过程中,了解过了原理之后,就是学习各种Payload,这里面蕴藏着丰富的知识含量,是在基本上覆盖了漏洞原理之后的进一步深入学习的必经之路.无理是Burpsuite还是Sqlmap.Awvs亦或是其他工具,包括人工收工构造的Payload都有很高的记录和学习意义,一方面如上所说的提高对WEB安全的掌握和理解,另一方面也对WEB安全自动化测试做积累. 需求: 记录WEB安全各种报文payload的工具 开发语言: Python2.7 依赖第三方库: pypcap dpkt

DNS隧道通信的检测

DNS隧道通信的检测 DNS 隧道通信 DNS 隧道通信是C&C常用的通信方式,一般常用的编码方式Base64,Binary编码,Hex编码等.且请求的Type一般都是txt(为了返回的时候能够加入更多的信息).payload部分一般是子域名.攻击者自己控一个域的DNS权威应答服务器,然后等待失陷主机请求域名,本地DNS服务器迭代查询转发请求到那台权威DNS,从而实现失陷主机与C&C Server的通信. DNS 检测方案 + 频率大:一般远超正常的DNS频率: + 不重复:一般子域名变化

大数据技术之_18_大数据离线平台_02_Nginx+Mysql+数据收集+Web 工程 JS/JAVA SDK 讲解+Flume 故障后-如何手动上传 Nginx 日志文件至 HDFS 上

十一.Nginx11.1.介绍11.2.常见其他 Web 服务器11.3.版本11.4.Nginx 安装11.5.目录结构11.6.操作命令十二.Mysql12.1.介绍12.2.关系型数据库(SQL)种类12.3.特征12.4.术语12.4.与非关系型数据库比较(Not Only SQL)12.4.1.种类12.4.2.特征12.4.3.总结十三.数据收集13.1.收集方式13.2.数据的事件类型13.2.1.Launch 事件13.2.2.PageView 事件13.3.Nginx 日志收集

记 Maven 本地仓库埋坑之依赖包为何不能用

记一次 Maven 本地仓库埋坑之 Verifying Availability 背景 某 Java 后端项目使用 maven 构建,因为某些原因,某些依赖库下载不了,直接找其它人索要了他电脑上的 maven 本地仓库里的依赖包. 然后直接拷贝到我电脑的本地 maven 仓库里,但构建项目时,发现,仍旧报找不到依赖包也下载不了的错误,导致项目构建不起来. 异常信息 以上是背景,下面是构建过程出现的一些异常: The Pom for xxx.jar is missing, no dependenc