- 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