我们先来看一下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
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(data_ - arr);
}
}
explicit SourceFile(const char* filename)
: data_(filename)
{
const char* slash = strrchr(filename, '/');
if (slash)
{
data_ = slash + 1;
}
size_ = static_cast(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()
#include
#include
#include
#include
#include
#include
#include
#include
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(microSecondsSinceEpoch / Timestamp::kMicroSecondsPerSecond);
int microseconds = static_cast(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;
}
//缓冲区大小的配置
const int kSmallBuffer = 4000;
const int kLargeBuffer = 4000*1000;
template
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(avail()) > len) //如果可用数据足够,就拷贝过去,同时移动当前指针。
{
memcpy(cur_, buf, len);
cur_ += len;
}
}
const char* data() const { return data_; } //返回首地址
int length() const { return static_cast(cur_ - data_); } //返回缓冲区已有数据长度
// write to data_ directly
char* current() { return cur_; } //返回当前数据末端地址
int avail() const { return static_cast(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 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(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(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
void formatInteger(T);
Buffer buffer_;
static const int kMaxNumericSize = 32;
};
class Fmt // : boost::noncopyable
{
public:
template
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
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
size_t convert(char buf[], T value)
{
T i = value;
char* p = buf;
do
{//lsd意思是last digit,最后一位数字
int lsd = static_cast(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(i % 16);
i /= 16;
*p++ = digitsHex[lsd];
} while (i != 0);
*p = '\0';
std::reverse(buf, p);
return p - buf;
}
template class FixedBuffer;
template class FixedBuffer;
}
}
template //使用非类型模板参数
const char* FixedBuffer::debugString()
{
*cur_ = '\0'; //加个\0把缓冲区作为字符串
return data_;
}
template
void FixedBuffer::cookieStart()
{
}
template
void FixedBuffer::cookieEnd()
{
}
void LogStream::staticCheck()
{
BOOST_STATIC_ASSERT(kMaxNumericSize - 10 > std::numeric_limits::digits10);
BOOST_STATIC_ASSERT(kMaxNumericSize - 10 > std::numeric_limits::digits10);
BOOST_STATIC_ASSERT(kMaxNumericSize - 10 > std::numeric_limits::digits10);
BOOST_STATIC_ASSERT(kMaxNumericSize - 10 > std::numeric_limits::digits10);
}
template
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(v);
return *this;
}
LogStream& LogStream::operator<<(unsigned short v)
{
*this << static_cast(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(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
Fmt::Fmt(const char* fmt, T val)
{
//断言是算术类型
BOOST_STATIC_ASSERT(boost::is_arithmetic::value == true);
//格式化到缓冲区中
length_ = snprintf(buf_, sizeof buf_, fmt, val);
assert(static_cast(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);