muduo网络库学习笔记(8):高效日志类的封装
来源:互联网 发布:matlab最优算法 编辑:程序博客网 时间:2024/06/06 20:55
前言
在服务端编程中,日志是必不可少的。
开发过程中,日志的存在能方便我们调试错误和更好地理解程序;运行过程中,日志能帮助我们诊断系统故障并处理、记录系统运行状态。
muduo日志类封装细节
(1)日志消息有多种级别(level),如TRACE、DEBUG、INFO、WARN、ERROR、FATAL。日志的输出级别在运行时可调。
代码片段1:返回当前日志级别文件名:Logging.ccLogger::LogLevel initLogLevel(){ if (::getenv("MUDUO_LOG_TRACE")) //获取环境变量MUDUO_LOG_TRACE return Logger::TRACE; else if (::getenv("MUDUO_LOG_DEBUG")) return Logger::DEBUG; else return Logger::INFO;}
(2)日志类Logger的使用流程
Logger使用时序图如下:
Logger类主要负责日志的级别等,它的内部嵌套类Impl则负责实际的实现。使用时,首先构造一个匿名的Logger对象,然后调用stream()函数返回一个LogStream对象,LogStream对象再调用重载的<<运算符来输出日志。事实上,日志先输出到缓冲区,然后才输出到标准输出或文件。匿名的Logger对象在销毁时调用析构函数,析构函数调用g_output和g_flush输出到日志对应的设备。
代码片段2:Logger的析构函数文件名:Logging.ccLogger::~Logger(){ impl_.finish(); const LogStream::Buffer& buf(stream().buffer()); //获取缓冲区 g_output(buf.data(), buf.length()); //默认输出到stdout //当日志级别为FATAL时,flush设备缓冲区中并终止程序 if (impl_.level_ == FATAL) { g_flush(); abort(); }}
Logger类的使用示例:
#define LOG_INFO if (muduo::Logger::logLevel() <= muduo::Logger::INFO) \ muduo::Logger(__FILE__, __LINE__).stream()LOG_INFO<<“info ...”; // 使用方式muduo::Logger(__FILE__, __LINE__).stream()<<“info”; //传递代码所在的文件名和行号参数
(3)重载<<运算符
以输出int类型的<<运算符为例,它并不是直接存放int类型的数据,而是转换为string类型后再存放到buffer:
代码片段3:重载<<运算符文件名:LogStream.cc......//通过调用convert函数将整数转换为字符串template<typename T>size_t convert(char buf[], T value){ T i = value; char* p = buf; do { int lsd = static_cast<int>(i % 10); //得到最后一个数字,last digit i /= 10; // const char digits[] = "9876543210123456789"; // const char* zero = digits + 9; // 假如此时获取的lsd值为5,指针zero指向digits[]中的'0' // zero[lsd]再偏移lsd即5个位置,便获取到了字符'5',保存到了buf中 *p++ = zero[lsd]; } while (i != 0); //为负数则添加负号 if (value < 0) { *p++ = '-'; } *p = '\0'; std::reverse(buf, p); //将字符串逆转 return p - buf;}......template<typename T>void LogStream::formatInteger(T v){ //kMaxNumericSize的值为32,即如果buffer的空间足够大 if (buffer_.avail() >= kMaxNumericSize) { size_t len = convert(buffer_.current(), v); buffer_.add(len); }}......LogStream& LogStream::operator<<(int v){ formatInteger(v); //调用formatInteger()函数 return *this; //返回LogStream对象的指针}
(4)FixedBuffer的设计
FixedBuffer的实现为一个模板类,传入一个非类型参数SIZE表示缓冲区的大小。通过对data_首地址、cur_指针、end()函数的组合调用完成缓冲区的各操作,例如:
代码片段4:模版类FixedBuffer的成员函数avail()返回当前可用的空间文件名:LogStream.hint avail() const { return static_cast<int>(end() - cur_); }
(5)日志滚动
muduo库日志滚动的条件通常有两个:
文件大小 - 例如每写满1G换下一个文件
时间 - 例如每天零点新建一个文件,不管前一个文件是否写满
I.日志文件文件名的设计
例:logfile_test.20120603-144022.hostname.3605.log
第一部分如“logfile_test”是日志文件的basename;
第二部分如“20120603-144022”是日志的创建时间(UTC时间);
第三部分如“hostname”是主机名称;
第四部分如“3605”是进程id;
最后是日志后缀名“.log”。
代码片段5:获取日志文件名文件名:LogFile.ccstring LogFile::getLogFileName(const string& basename, time_t* now){ string filename; //预留basename的size加上64字节的空间 filename.reserve(basename.size() + 64); filename = basename; char timebuf[32]; char pidbuf[32]; struct tm tm; *now = time(NULL); gmtime_r(now, &tm); // 线程安全,获取日志创建时间 strftime(timebuf, sizeof timebuf, ".%Y%m%d-%H%M%S.", &tm); //将时间格式化 filename += timebuf; filename += ProcessInfo::hostname(); //用到了gethostname()返回主机名 snprintf(pidbuf, sizeof pidbuf, ".%d", ProcessInfo::pid()); filename += pidbuf; filename += ".log"; return filename;}
II.日志的滚动实现
代码片段6:日志的滚动文件名:LogFile.ccvoid LogFile::rollFile(){ time_t now = 0; string filename = getLogFileName(basename_, &now); //注意,这里先除以kRollPerSeconds_ 后乘kRollPerSeconds_表示 //对齐至kRollPerSeconds_(24*60*60)整数倍,也就是时间调整到当天零点。 time_t start = now / kRollPerSeconds_ * kRollPerSeconds_; //如果now大于上一次滚动日志文件时间就滚动 if (now > lastRoll_) { lastRoll_ = now; //lastRoll_是上一次滚动日志文件时间 lastFlush_ = now; //lastFlush_是上一次日志写入文件时间 startOfPeriod_ = start; //startOfPeriod_是开始记录日志时间(调整至零点的时间) file_.reset(new File(filename)); }}
代码片段7:写入日志时,判断是否需要滚动日志文件名:LogFile.ccvoid LogFile::append_unlocked(const char* logline, int len){ file_->append(logline, len); //写入的字节数大于rollSize_时要滚动 if (file_->writtenBytes() > rollSize_) { rollFile(); } else { //计数值count_超过kCheckTimeRoll_时也要判断是否需要滚动 if (count_ > kCheckTimeRoll_) { count_ = 0; time_t now = ::time(NULL); time_t thisPeriod_ = now / kRollPerSeconds_ * kRollPerSeconds_; if (thisPeriod_ != startOfPeriod_) { rollFile(); } //大于flush的间隔时间时则写入日志,不滚动 else if (now - lastFlush_ > flushInterval_) { lastFlush_ = now; file_->flush(); } } else { ++count_; } }}
- muduo网络库学习笔记(8):高效日志类的封装
- muduo日志类的封装
- muduo网络库源码学习————日志类封装
- muduo网络库学习笔记(5):线程池的实现
- muduo网络库学习笔记(6):单例类(线程安全的)
- muduo网络库学习笔记(10):定时器的实现
- muduo源码学习(11)-日志类封装1
- muduo源码学习(12)-日志类封装2
- muduo网络库学习笔记(1):Timestamp类
- muduo网络库学习笔记(3):Thread类
- muduo网络库学习笔记(12):TcpServer和TcpConnection类
- muduo网络库学习(二)对套接字和监听事件的封装Channel
- muduo网络库学习之MutexLock类、MutexLockGuard类、Condition类、CountDownLatch类封装中的知识点
- muduo网络库学习之BlockinngQueue<T>类、ThreadPool 类、Singleton类封装中的知识点
- muduo网络库学习之Logger类、LogStream类、LogFile类封装中的知识点
- muduo 6 网络库学习之BlockinngQueue<T>类、ThreadPool 类、Singleton类封装中的知识点
- muduo网络库学习之Timestamp类、AtomicIntegerT 类封装中的知识点
- muduo网络库学习之Exception类、Thread 类封装中的知识点(重点讲pthread_atfork())
- 自行编写的Makefile文件的结构
- XML 常用操作
- 重建二叉树
- gdb使用方法总结
- Linux Tomcat报错: java.lang.IllegalStateException: Cannot run without an instance id.
- muduo网络库学习笔记(8):高效日志类的封装
- Java使用mysql-jdbc连接MySQL出现的错误
- 解决导入的Android项目中出现的The project was not built since和 Unable to resolve target 'android-19'错误
- mongodb
- TOJ 1859.Goldbach's Conjecture
- 死锁
- foxscan的安装笔记~
- C++中异常处理中的异常重新抛出的一种用法
- JAVA基础day04 数组学习 排序和查找基本算法