第四章 日志系统
-
/var/log/下查看VMware中Ubuntu的日志
-
cpp学习必备:gitee上面搜awesome -cpp
-
日志系统构成
日志来源
系统控制
日志输出
日志存储
-
日志系统设计
记录器
过滤器
格式化器
输出器
-
一条日志的生命周期
-
产生。info(“log information.”);
-
经过记录器。记录器去获取日志发生的时间,位置,线程信息等等信息,会有一个数据结构去存储你需要的信息(例如:msg:”log information.”,time:2018- 3-20 10:00:00,level:info,location:main.rs:3 lines)
-
经过过滤器。决定是否记录(例如,过滤条件设为info级以下的过滤掉,这条日志信息等级是info,满足条件,继续。)
-
经过格式化器。假设我们想输出为“2018-3-22 10:00:00 [info] log information.”
-
到输出器。例如输出到文件中,我们就将这条信息写到文件上 ;
-
这条日志信息生命结束了。
-
-
使用日志系统log4cpp
-
日志的格式
涉及头文件:
#include<log4cpp/BasicLayout.hh> #include<log4cpp/SimpleLayout.hh> #include<log4cpp/PatternLayout.hh>
BasicLayout:“以时间戳 + 优先级 + 内容”
//构造函数: log4cpp::BasicLayout::BasicLayout() //输出例子格式: 1248337987 ERROR : Hello log4cppin a Error Message! 1248337987 WARN : Hello log4cppin a Warning Message!
SimpleLayout:“优先级 + 日志信息”
//构造函数: log4cpp::SimpleLayout::SimpleLayout()
PatterLayout:“使用类似printf的格式化模式”
//构造函数: log4cpp::PatternLayout::PatternLayout(); //PatterLayout 设置日志格式函数 void log4cpp::PatternLayout::setConversionPattern(const std::string &conversionPattern); //使用 PatterLayout 时的格式化参数: %c category; %d 日期;日期可以进一步的设置格式,用花括号包围,例如%d{%H:%M:%S,%l} 或者 %d{%d %m %Y%H:%M:%S,%l}。如果不设置具体日期格式,则如下默认格式被使用“Wed Jan 02 02:03:55 1980”。 日期的格式符号与 ANSI C 函数 strftime 中的一致。但增加了一个格式符号%l,表示毫秒,占三个十进 制位。 %m 消息; %n 换行符,会根据平台的不同而不同,但对于用户透明; %p 优先级; %r 自从 layout 被创建后的毫秒数; %R 从 1970 年 1 月 1 日 0 时开始到目前为止的秒数; %u 进程开始到目前为止的时钟周期数; %x NDC。 //举例: log4cpp::PatternLayout* pLayout = new log4cpp::PatternLayout(); pLayout->setConversionPattern("%d: [%p] %c %x: %m%n");
-
日志的目的地
涉及头文件:
#include <log4cpp/Appender.hh> #include <log4cpp/OstreamAppender.hh> #include <log4cpp/StringQueueAppender.hh> #include <log4cpp/FileAppender.hh> #include <log4cpp/RollingFileAppender.hh>
OstreamAppender:输出到一个ostream类
//构造函数: log4cpp::OstreamAppender::OstreamAppender(const std::string &name, std::ostream stream); //参数: //name:the name of the Appender //stream:the name of the ostream log4cpp::OstreamAppender osAppender = new log4cpp::OstreamAppender("osAppender", &cout);
StringQueueAppender:内存队列
//构造函数: log4cpp::StringQueueAppender::StringQueueAppender(const std::string &name); //举例: log4cpp::StringQueueAppender* strQAppender = new log4cpp::StringQueueAppender("strQAppender");
FileAppender:输出到文件
//构造函数: log4cpp::FileAppender::FileAppender (const std::string &name, int fd); /*参数: name:he name of the Appender. fd:the file descriptor to which the Appender has to log. */ //构造函数 log4cpp::FileAppender::FileAppender (const std::string &name, const std::string &fileName, bool append = true, mode_t mode = 00644); /*参数: name:the name of the Appender. fileName:the name of the file to which the Appender has to log. append:whether the Appender has to truncate the file or just append to it if it already exists. Defaults to 'true'. mode:file mode to open the logfile with. Defaults to 00644. */ //举例: log4cpp::FileAppender* fileAppender = new log4cpp::FileAppender("fileAppender","wxb.log"); fileAppender->setLayout(pLayout1);
Appender 有个成员函数(方法):
virtual void log4cpp::Appender::setLayout(Layout *layout) //函数用作:设置 appender 格式
RoollingFileAppender:滚动式输出到文件
//构造函数 log4cpp::RollingFileAppender::RollingFileAppender(const std::string &name, const std::string &fileName, size_t maxFileSize = 10 *1024 *1024, unsigned int maxBackupIndex = 1, bool append = true, mode_t mode = 00644); /* 参数: 前两个参数与普通文件的类似 第三个参数 maxFileSize:指出了回滚文件的最大值 第四个参数 maxBackupIndex:指出了回滚文件所用的备份文件的最大个数。所谓备份文件,是用来保存回滚文件中因为空间不足未能记录的日志,备份文件的大小仅比回滚文件的最大值大 1kb。所以如果 maxBackupIndex 取值为 3,则回滚文件(假设其名称是 rollwxb.log,大小为 100kb)会有三个备份文件,其名称分别是 rollwxb.log.1,rollwxb.log.2 和 rollwxb.log.3,大小为 101kb。另外要注意:如果 maxBackupIndex 取值为 0 或者小于 0,则回滚文件功能会失效,其表现如同 FileAppender 一样,不会有大小的限制。这也许是一个bug。 第五第六参数同上。 */ //举例: log4cpp::RollingFileAppender* rollfileAppender = new log4cpp::RollingFileAppender( "rollfileAppender","rollwxb.log",5*1024,3); rollfileAppender->setLayout(pLayout2);
-
Category日志的种类
涉及头文件
#include<log4cpp/Category.hh>
Log4cpp 中有一个总是可用并实例化好的 Category,即根 Category。使用log4cpp::Category::getRoot()可以得到根 Category。
在大多数情况下,一个应用程序只需要一个日志种类(Category),但是有时也会用到多个 Category,此时可以使用根 Category 的 getInstance 方法来得到子 Category。不同的子 Category 用于不同的场合。
log4cpp::Category& root =log4cpp::Category::getRoot(); log4cpp::Category& infoCategory =root.getInstance("infoCategory"); infoCategory.addAppender(osAppender); infoCategory.setPriority(log4cpp::Priority::INFO);
主要的成员函数:
//设置category的级别 void log4cpp::Category::setPriority (Priority::Value priority); //设置或添加appender void log4cpp::Category::addAppender (Appender *appender ); void log4cpp::Category::setAppender (Appender *appender ); //相应日志级别的记录 //emerg/fatal/alter/crit/error/warn/notice/info/debug/notset void log4cpp::Category::warn(const std::string &message); void log4cpp::Category::warn(const char * stringFormat,... ); void log4cpp::Category::error(const std::string &message); void log4cpp::Category::error(const char *stringFormat,... );
-
日志的优先级(当日志的优先级比category的优先级低的时候,日志就会被过滤)
涉及头文件:
#include<log4cpp/Priority.hh>
每个Category都有一个优先级,该优先级可以由setPriority方法设置,或者从其父 Category 中继承而来。每条日志也有一个优先级,当 Category 记录该条日志时,若日志优先级高于 Category 的优先级时,该日志被记录,否则被忽略。
-
程序员在使用 log4cpp时应该遵循以下几个使用原则:
1)不要手动释放 Category、Appender 和 Layout;
2)同一个 Appender 不要加入多个 Category,否则它会被释放多次从而导致程序崩溃;
3)同一个 Layout 不要附着到多个 Appender 上,否则也会被释放多次导致程序崩溃;
-
log4cpp::Category::shutdown():在不使用 log4cpp 时可调用 log4cpp::Category::shutdown(),其功能如同 HierarchyMaintainer 的内存清理。但如果不手动调用,在程序结束时 HierarchyMaintainer 会调用 Category 的析构函数来释放所有 Appender。
-
使用举例
void test() { //日志的格式 PatternLayout *ppl1 = new PatternLayout(); ppl1->setConversionPattern("%d %c [%p] %m%n"); PatternLayout *ppl2 = new PatternLayout(); ppl2->setConversionPattern("%d %c [%p] %m%n"); //日志的目的地 OstreamAppender *pos = new OstreamAppender("OstreamAppender", &cout); pos->setLayout(ppl1); RollingFileAppender *pfa = new RollingFileAppender("RollingFileAppender1234", "wd.log", 3 * 1024, 3); pfa->setLayout(ppl2); //日志的种类 Category &root = Category::getRoot().getInstance("mycat"); root.addAppender(pfa); root.addAppender(pos); root.setPriority(Priority::ERROR);//日志的优先级 //当日志的优先级比category的优先级低的时候,日志就会被过滤 size_t idx = 0; while(idx < 100) { root.emerg("this is an emerg"); root.fatal("this is an fatal"); root.error("this is a error"); root.warn("this is a warn "); root.debug("this is a debug"); ++idx; } //回收资源 Category::shutdown(); }
-
Log4cpp的封装[单例模式,可变参数]。
//Mylogger.hpp #ifndef __WD_MYLOGGER_H__ #define __WD_MYLOGGER_H__ #include <log4cpp/Category.hh> #include <string> using std::string; namespace wd { class Mylogger { public: enum Priority { ERROR = 300, WARN, INFO, DEBUG }; static Mylogger * getInstance(); static void destroy(); template <class... Args> void warn(const char * msg, Args... args) { _mycat.warn(msg, args...); } template <class... Args> void error(const char * msg, Args... args) { _mycat.error(msg, args...); } template <class... Args> void info(const char * msg, Args... args) { _mycat.info(msg, args...); } template <class... Args> void debug(const char * msg, Args... args) { _mycat.debug(msg, args...); } void warn(const char * msg); void error(const char * msg); void info(const char * msg); void debug(const char * msg); void setPriority(Priority p); private: Mylogger(); ~Mylogger(); private: log4cpp::Category & _mycat; static Mylogger * _pInstance; }; #define prefix(msg) string("[")\ .append(__FILE__).append(":")\ .append(__FUNCTION__).append(":")\ .append(std::to_string(__LINE__)).append("] ")\ .append(msg).c_str() #define LogError(msg, ...) Mylogger::getInstance()->error(prefix(msg), ##__VA_ARGS__) #define LogWarn(msg, ...) Mylogger::getInstance()->warn(prefix(msg), ##__VA_ARGS__) #define LogInfo(msg, ...) Mylogger::getInstance()->info(prefix(msg), ##__VA_ARGS__) #define LogDebug(msg, ...) Mylogger::getInstance()->debug(prefix(msg), ##__VA_ARGS__) }//end of namespace wd #endif //Mylogger.cc #include "Mylogger.hpp" #include <log4cpp/PatternLayout.hh> #include <log4cpp/Priority.hh> #include <log4cpp/OstreamAppender.hh> #include <log4cpp/FileAppender.hh> #include <iostream> using std::cout; using std::endl; namespace wd { Mylogger * Mylogger::_pInstance = nullptr; Mylogger * Mylogger::getInstance() { if(nullptr == _pInstance) { _pInstance = new Mylogger(); } return _pInstance; } void Mylogger::destroy() { if(_pInstance) { delete _pInstance; } } Mylogger::Mylogger() : _mycat(log4cpp::Category::getRoot().getInstance("MyCategory")) { using namespace log4cpp; PatternLayout * ptn1 = new PatternLayout(); ptn1->setConversionPattern("%d %c [%p] %m%n"); PatternLayout * ptn2 = new PatternLayout(); ptn2->setConversionPattern("%d %c [%p] %m%n"); OstreamAppender * ostreamAppender = new OstreamAppender("OstreamAppender", &cout); ostreamAppender->setLayout(ptn1); FileAppender * fileAppender = new FileAppender("FileAppender", "wd.log"); fileAppender->setLayout(ptn2); _mycat.addAppender(ostreamAppender); _mycat.addAppender(fileAppender); _mycat.setPriority(log4cpp::Priority::DEBUG); cout << "Mylogger()" << endl; } Mylogger::~Mylogger() { cout << "~Mylogger()" << endl; log4cpp::Category::shutdown(); } void Mylogger::warn(const char * msg) { _mycat.warn(msg); } void Mylogger::error(const char * msg) { _mycat.error(msg); } void Mylogger::info(const char * msg) { _mycat.info(msg); } void Mylogger::debug(const char * msg) { _mycat.debug(msg); } void Mylogger::setPriority(Priority p) { switch(p) { case WARN: _mycat.setPriority(log4cpp::Priority::WARN); break; case ERROR: _mycat.setPriority(log4cpp::Priority::ERROR); break; case INFO: _mycat.setPriority(log4cpp::Priority::INFO); break; case DEBUG: _mycat.setPriority(log4cpp::Priority::DEBUG); break; } } }//end of namespace wd //TestMylogger.cc #include "Mylogger.hpp" #include <iostream> #include <string> using std::cout; using std::endl; using std::string; using namespace wd; void test0() { Mylogger::getInstance()->error(prefix("this is an error message!")); Mylogger::getInstance()->warn("this is a warn message!"); Mylogger::getInstance()->info("this is an info message!"); Mylogger::getInstance()->debug("this is a debug message!"); cout << "Mylogger::Priority::WARN = " << Mylogger::Priority::WARN << endl; cout << "Mylogger::Priority::ERROR = " << Mylogger::Priority::ERROR << endl; cout << "Mylogger::Priority::INFO = " << Mylogger::Priority::INFO << endl; cout << "Mylogger::Priority::DEBUG = " << Mylogger::Priority::DEBUG << endl; Mylogger::getInstance()->setPriority(Mylogger::Priority::WARN); } void test1() { cout << __FILE__ << " " << __LINE__ << " " << __FUNCTION__ << endl; } void test2() { LogError("this is a error message! "); LogWarn("this is a error message!"); LogInfo("this is a error message!"); LogDebug("this is a error message!"); } void test3() { int number = 10; const char * pstr = "hello,world"; Mylogger::getInstance()->debug(prefix("this is a error message! number = %d, str = %s"), number, pstr); LogError("this is a error message! number = %d, str = %s", number, pstr); LogWarn("this is a warn message!"); LogInfo("this is a info message!"); LogDebug("this is a error message!"); } int main(void) { //test0(); //test1(); //test2(); test3(); return 0; }
-