muduo库阅读(18)——日志记录器

来源:互联网 发布:淘宝新手开店怎么装修 编辑:程序博客网 时间:2024/05/17 23:52
namespace muduo{// 时区类class TimeZone;/* * 日志记录器 * 日记记录器只与日志流类(LogStream)、固定缓冲区(FixedBuffer)类有关、与日志文件类(LogFile)无关!!! * 只有当日志需要被记录到日志文件中的时候LogFile类才和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<int N>inline SourceFile(const char (&arr)[N]): data_(arr),  size_(N-1){// 获取文件名而不是路径// 因此从filename后面开始查找,找到路径分隔符,取出文件名const char* slash = strrchr(data_, '/'); // builtin functionif (slash){data_ = slash + 1;size_ -= static_cast<int>(data_ - arr);}}explicit SourceFile(const char* filename): data_(filename){// 获取文件名而不是路径// 因此从filename后面开始查找,找到路径分隔符,取出文件名const char* slash = strrchr(filename, '/');if (slash){data_ = slash + 1;}size_ = static_cast<int>(strlen(data_));}const char* data_;int size_;};// file表示产生日志的源文件,line表示行数,level表示日志级别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);// 输出回调函数,这个函数用于决定日志输出到哪,是日志文件还是控制台// 如果是输出到日志文件中,可以调用LogFile的Append函数// 默认调用的是defaultOutput函数typedef void (*OutputFunc)(const char* msg, int len);// 刷新回调函数// 默认调用的是defaultFlushtypedef 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_;// 日志流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";//// 定义各类用于记录日志的宏!!!// 追踪#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;}}

namespace muduo{/* * 下面三个变量加上__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;elsereturn Logger::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_);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);//FIXME check n(void)n;}// 默认的情况下,刷新控制台void defaultFlush(){fflush(stdout);}// 设置日志记录器的默认输出/刷新函数Logger::OutputFunc g_output = defaultOutput;Logger::FlushFunc g_flush = defaultFlush;// 全局的时区TimeZone g_logTimeZone;}using namespace muduo;/* * 下面是日志记录器的实现类 */Logger::Impl::Impl(LogLevel level, int savedErrno, const SourceFile& file, int line): time_(Timestamp::now()),  stream_(),  level_(level),  line_(line),  basename_(file){// 格式化时间formatTime();// 缓存当前的线程idCurrentThread::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,数据来自日志流的bufferconst LogStream::Buffer& buf(stream().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;}


0 0
原创粉丝点击