目录

  • 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

下面为默认的OutputFuncFlushFunc

//默认为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文件相关推荐

  1. muduo学习笔记:base部分之高性能日志库

    服务端编程,日志必不可少,生产环境中应做到"Log Everything All The Time". 一个日志库答题分为前端(Logging.{h,cc})和后端(LogFile ...

  2. WLog日志库:c++ 高拓展、高性能日志库

    日志库实现 该日志库是我借鉴sylar和muduo网络库所研究出来的具有高扩展性和高性能的跨平台的日志库.该日志库使用流式输出,只支持日志输出到文件,原因是网络io很不稳定,基本没有见过利用网络传输进 ...

  3. 深度|从Go高性能日志库zap看如何实现高性能Go组件

    导语:zap是uber开源的Go高性能日志库.本文作者深入分析了zap的架构设计和具体实现,揭示了zap高效的原因.并且对如何构建高性能Go语言库给出自己的建议. 作者简介:李子昂,美图公司架构平台系 ...

  4. 深度 | 从Go高性能日志库zap看如何实现高性能Go组件

    导语:zap是uber开源的Go高性能日志库.本文作者深入分析了zap的架构设计和具体实现,揭示了zap高效的原因.并且对如何构建高性能Go语言库给出自己的建议. 作者简介:李子昂,美图公司架构平台系 ...

  5. golang高性能日志库zap的使用

    本文作者:陈进坚 个人博客:https://jian1098.github.io CSDN博客:https://blog.csdn.net/c_jian 简书:https://www.jianshu. ...

  6. 号称C/C++高性能日志库

    ** 以下都是号称高性能日志库 ** 腾讯 C++日志库:Mars XLOG https://github.com/Tencent/mars 美团 C/C++日志库 https://github.co ...

  7. Golang高性能日志库zap + lumberjack 日志切割组件详解

    文章篇幅较长,可以先收藏防止迷路~ 目录 zap日志库 1. why zap? 2. 简单使用 3. 自定义logger例子 4. Gin项目使用zap 6. lumberjack 日志切割组件 za ...

  8. 【c++】SPDLOG动态库和静态库、异步日志库hang 问题、registry核心类

    spdlog 官方支持动态库和静态库 静态库 SPDLOG_COMPILED_LIB SPDLOG_API 这个就是空的 静态库里 SPDLOG_INLINE 也是空的: 动态库: SPDLOG_SH ...

  9. Golang一日一库之 日志库 zap

    简介 在开发过程中 会使用到日志库去记录错误的日志,尤其是golang中 有无穷无尽的error 如果不记录,当你的代码出错,就无从排错了. zap 是开源的 Go 高性能日志库 主要有以下特点: 支 ...

最新文章

  1. 十年JAVA架构经验总结:这几点尤为关键!
  2. 一键生成HTML4和WAP站
  3. Web 趋势榜: 上周不可错过的最热门的 10 大 Web 项目 - 又增加了那么多的好项目啊 - 210611...
  4. 2019届互联网校招本科薪酬清单
  5. html载入图片代码,TextVeiw加载HTML代码块内图片
  6. 微信iOS版本推出深色模式,网友:终于可以“好好熬夜”了!
  7. 微信小程序引用php函数,微信小程序Page中data数据操作和函数调用详细介绍
  8. 【PYTHON笔记】文件读写,定位
  9. scp实现mac与linux服务器之间文件传输
  10. 拓展卡尔曼滤波器(EKF)的数学推导
  11. 大数据技术全解之曹冲称象与大数据思想
  12. ajax send()的作用_AJAX(Asynchronous JavaScript And XML)
  13. 尚硅谷李玉婷老师mysql课程--数据库和SQL概述
  14. Turbo码基本框架
  15. OSx86的来龙去脉
  16. oracle数据库表空间如何清理,oracle数据库清理临时表空间
  17. 分布式事务之——基于消息中间件实现
  18. 特此郑重声明!我的文章全部是原创作品!转载请注明出处!
  19. ARM寄存器的7种工作模式和几种寻址方式
  20. input输入数字金额

热门文章

  1. CheckMarx Cross Site History Manipulation 解决方案
  2. Google Play Store Apps(谷歌应用程序相关数据集)
  3. 什么是扫描件PDF?扫描件PDF如何转换成可编辑文本?
  4. PHP 页面跳转到另一个页面的多种方法方法总结
  5. c# ushort_C#中的ushort关键字
  6. ABBYY FineReader 14的新增功能有哪些?
  7. Windows安装Go语言开发环境+配置
  8. Cena、Lemon自动AC机
  9. NFS服务器搭建及配置
  10. android crt证书,如何在android手机安装数字证书 crt和p12