muduo库的高性能日志库(三)——Logging文件
目录
- SourceFile内部类
- Impl内部类
- 内部实现细节
- Logger类
- 1. 规定日志的几个等级
- 2. 两个内部类
- 3. 设置日志属性
- 4. 构造函数
- 5. 析构函数
- 日志宏
接下看一下Logging文件,该文件主要负责全局日志级别,输出目的地设置
Logger内有两个内部类
SourceFile
,主要负责获取文件名
Impl
,实际用于日志消息的处理类
SourceFile内部类
该类就负责获取基文件名
eg:
/home/gty/muduo/muduo/base/test.cc
对应的基文件名就是
test.cc
实现很简单,调用了c语言库函数strrchr
C 库函数
char *strrchr(const char *str, int c)
在参数 str 所指向的字符串中搜索最后一次
出现字符 c(一个无符号字符)的位置
class SourceFile{//找到基文件的文件名,并将data_指向基文件名的开始,size_存文件名长度public:template<int N>SourceFile(const char (&arr)[N]) //数组引用: data_(arr),size_(N-1){//C 库函数 char *strrchr(const char *str, int c) //在参数 str 所指向的字符串中搜索最后一次出现字符 c(一个无符号字符)的位置const char* slash = strrchr(data_, '/'); // builtin functionif (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_;int size_;};
Impl内部类
该类就是指定了一条日志的具现化
|----日志打印时间
|----日志缓存(LogStream)
|----日志的level
|----行号
|----文件名称
具体如下
//内部类,实际用于日志消息的处理类
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_; //当前记录日志宏的源代码名称
};
下面看看该类的实现细节
内部实现细节
__thread char t_errnobuf[512];
__thread char t_time[64]; //当前线程的时间字符串“年:月:日:时:分:秒”
__thread time_t t_lastSecond; //当前线程上一次日志记录时的秒数//Impl的构造函数
Logger::Impl::Impl(LogLevel level, int savedErrno, const SourceFile& file, int line): time_(Timestamp::now()),//登记当前时间stream_(), //Logstream类level_(level), //日志级别line_(line), //行号basename_(file) //文件名称
{formatTime(); //格式化时间CurrentThread::tid(); //缓存当前线程tid//将当前线程id和日志等级名称输出到buf当中stream_ << T(CurrentThread::tidString(), CurrentThread::tidStringLength());stream_ << T(LogLevelName[level], 6);if (savedErrno != 0) //如果savedErrno不为0,saveErrno为错误号{//将错误码对应的错误信息存入t_errnobuf当中,并且输入到stream_的buf当中stream_ << strerror_tl(savedErrno) << " (errno=" << savedErrno << ") ";}
}//格式化时间
void Logger::Impl::formatTime()
{int64_t microSecondsSinceEpoch = time_.microSecondsSinceEpoch();//获取微秒格式时间//获得秒,kMicroSecondsPerSecond为1000000time_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);}
}//所有信息输出完毕,然后再将文件名称和行号输出到buf当中
void Logger::Impl::finish()
{stream_ << " - " << basename_ << ':' << line_ << '\n';
}
Logger类
Logger的两个内部类看完,分析这个类
1. 规定日志的几个等级
|----TRACE|----DEBUG|----INFO|----WARN|----ERROR|----FATAL|----NUM_LOG_LEVELS
代码如下:
enum LogLevel{TRACE,DEBUG,INFO,WARN,ERROR,FATAL,NUM_LOG_LEVELS,};
2. 两个内部类
|----SourceFile (文件名截取)
|----Impl (实际日志信息处理类)
3. 设置日志属性
|----setLogLevel(***) //设置日志等级
|----setOutput(***) //设置输出函数
|----setFlush(***) //设置刷新函数
|----setTimeZone(***) //设置日期格式
代码如下:
//设置日志级别static void setLogLevel(LogLevel level);//设置输出函数(默认的输出函数是输出到stdout)typedef void (*OutputFunc)(const char* msg, int len);//设置刷新函数(默认的刷新函数是刷新到stdout)typedef void (*FlushFunc)();static void setOutput(OutputFunc); //默认fwrite到stdoutstatic void setFlush(FlushFunc); //默认fflush到stdoutstatic void setTimeZone(const TimeZone& tz); 默认 GMT
下面为默认的OutputFunc
和FlushFunc
//默认为stdout
void defaultOutput(const char* msg, int len)
{size_t n = fwrite(msg, 1, len, stdout);//FIXME check n(void)n;
}//默认为stdout
void defaultFlush()
{fflush(stdout);
}
//设置默认的输出和刷新函数
Logger::OutputFunc g_output = defaultOutput;
Logger::FlushFunc g_flush = defaultFlush;
TimeZone g_logTimeZone;
初始化日志等级
//初始化日至等级
Logger::LogLevel initLogLevel()
{//getenv()用来取得参数name环境变量的内容。//参数name为环境变量的名称,如果该变量存在则会返回指向该内容的指针。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();
4. 构造函数
有四个重载
四种不同类型的日志格式
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);
5. 析构函数
日志打印的基本所有操作都是在析构中进行的
//主要在析构进行
Logger::~Logger()
{impl_.finish();const LogStream::Buffer& buf(stream().buffer());g_output(buf.data(), buf.length());if (impl_.level_ == FATAL){//如果这是一个FATAL刷新缓冲区,然后直接中断程序g_flush();abort();}
}
日志宏
使用LOG_*之类的宏会创建一个临时匿名Logger对象,这个对象有一个Impl对象,而Impl对象有一个LogStream对象。LOG_*宏会返回一个LogStream对象的引用。用于将内容输入到LogStream中的一个buffer中。
在Logger的析构函数中,调用 g_output,即 g_output(buf.data(), buf.length()),将存于LogStream的buffer的日志内容输出。如果是FATAL错误,还要调用g_flush,最后abort()程序。如果没有调用g_flush,会一直输出到缓冲区(标准输出缓冲区,文件FILE缓冲区)满才会真的输出在标准输出,或者写入到文件中去。
//日志宏
#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()
muduo库的高性能日志库(三)——Logging文件相关推荐
- muduo学习笔记:base部分之高性能日志库
服务端编程,日志必不可少,生产环境中应做到"Log Everything All The Time". 一个日志库答题分为前端(Logging.{h,cc})和后端(LogFile ...
- WLog日志库:c++ 高拓展、高性能日志库
日志库实现 该日志库是我借鉴sylar和muduo网络库所研究出来的具有高扩展性和高性能的跨平台的日志库.该日志库使用流式输出,只支持日志输出到文件,原因是网络io很不稳定,基本没有见过利用网络传输进 ...
- 深度|从Go高性能日志库zap看如何实现高性能Go组件
导语:zap是uber开源的Go高性能日志库.本文作者深入分析了zap的架构设计和具体实现,揭示了zap高效的原因.并且对如何构建高性能Go语言库给出自己的建议. 作者简介:李子昂,美图公司架构平台系 ...
- 深度 | 从Go高性能日志库zap看如何实现高性能Go组件
导语:zap是uber开源的Go高性能日志库.本文作者深入分析了zap的架构设计和具体实现,揭示了zap高效的原因.并且对如何构建高性能Go语言库给出自己的建议. 作者简介:李子昂,美图公司架构平台系 ...
- golang高性能日志库zap的使用
本文作者:陈进坚 个人博客:https://jian1098.github.io CSDN博客:https://blog.csdn.net/c_jian 简书:https://www.jianshu. ...
- 号称C/C++高性能日志库
** 以下都是号称高性能日志库 ** 腾讯 C++日志库:Mars XLOG https://github.com/Tencent/mars 美团 C/C++日志库 https://github.co ...
- Golang高性能日志库zap + lumberjack 日志切割组件详解
文章篇幅较长,可以先收藏防止迷路~ 目录 zap日志库 1. why zap? 2. 简单使用 3. 自定义logger例子 4. Gin项目使用zap 6. lumberjack 日志切割组件 za ...
- 【c++】SPDLOG动态库和静态库、异步日志库hang 问题、registry核心类
spdlog 官方支持动态库和静态库 静态库 SPDLOG_COMPILED_LIB SPDLOG_API 这个就是空的 静态库里 SPDLOG_INLINE 也是空的: 动态库: SPDLOG_SH ...
- Golang一日一库之 日志库 zap
简介 在开发过程中 会使用到日志库去记录错误的日志,尤其是golang中 有无穷无尽的error 如果不记录,当你的代码出错,就无从排错了. zap 是开源的 Go 高性能日志库 主要有以下特点: 支 ...
最新文章
- 十年JAVA架构经验总结:这几点尤为关键!
- 一键生成HTML4和WAP站
- Web 趋势榜: 上周不可错过的最热门的 10 大 Web 项目 - 又增加了那么多的好项目啊 - 210611...
- 2019届互联网校招本科薪酬清单
- html载入图片代码,TextVeiw加载HTML代码块内图片
- 微信iOS版本推出深色模式,网友:终于可以“好好熬夜”了!
- 微信小程序引用php函数,微信小程序Page中data数据操作和函数调用详细介绍
- 【PYTHON笔记】文件读写,定位
- scp实现mac与linux服务器之间文件传输
- 拓展卡尔曼滤波器(EKF)的数学推导
- 大数据技术全解之曹冲称象与大数据思想
- ajax send()的作用_AJAX(Asynchronous JavaScript And XML)
- 尚硅谷李玉婷老师mysql课程--数据库和SQL概述
- Turbo码基本框架
- OSx86的来龙去脉
- oracle数据库表空间如何清理,oracle数据库清理临时表空间
- 分布式事务之——基于消息中间件实现
- 特此郑重声明!我的文章全部是原创作品!转载请注明出处!
- ARM寄存器的7种工作模式和几种寻址方式
- input输入数字金额