应用扩展2:日志系统log4cpp
一、日志的重要性
- 服务器程序:7*24小时,一直在运行——守护进程(后台默默运行)
· 当服务器崩了或者出问题之后,可以查日志
二、日志系统的构成
- 一个日志系统根据他的过程,分为4个模块,并加以抽象:
①日志来源:内核、虚拟机、邮箱等
②系统控制:流转过程(日志是怎样一步一步走动的?)
③日志输出:输出到文件?屏幕?
④日志存储:单独的系统?写入文件?
三、日志系统的设计思路
- 为了设计一个灵活可扩展、可配置的日志库,主要将日志库分为4个部分:
4个部分 | 作用 |
---|---|
记录器 | 记录日志的(原始信息、日志等级、时间、位置) |
过滤器 | 不需要的日志过滤掉 |
格式化器 | 给日志设置格式 |
输出器 | 日志输出到哪里? |
四、log4cpp
- 什么是log4cpp (log for cpp),记录日志的一种手段
- log4cpp的官网
(1)通过github.com(国外网站)到达log4cpp官网
(2)log4cpp官网:https://log4cpp.sourceforge.net/
(3)api:https://log4cpp.sourceforge.net/api/index.html
(4)class:https://log4cpp.sourceforge.net/api/hierarchy.html
(一)log4cpp的安装
1.下载log4cpp-1.1.1.tar.gz
2. 安装:先将log4cpp-1.1.1.tar.gz拖入用户主目录(~),
然后再执行以下步骤:
$ tar zxvf log4cpp-1.1.1.tar.gz
$ cd ~/log4cpp/
$ ./configure
$ make
$ make check
$ sudo make install
这里已经安装成功.
默认lib库路径是 : /usr/local/lib/
默认头文件的位置: /usr/local/include/log4cpp
(自己装在了/home/duxiuhan/log4cpp/include/log4cpp)
3. 使用:
3.1 编译使用log4cpp库的CPP文件时,要加上库文件,才能顺利的编译通过,如下示例
$ g++ log4test.cpp -llog4cpp -lpthread
3.2 运行时,如若提示缺少log4cpp库文件,表示找不到log4cpp的动态库,需要进行以下设置
以管理员身份登录终端,然后执行以下操作:
a. 添加/usr/local/lib到最后一行:$ sudo vim /etc/ld.so.conf
b. 在打开的文件末尾另起一行添加动态库log4cpp的路径(这里是/usr/local/lib),然后保存退出;
执行命令ldconfig使设置生效即可。
c. $ sudo ldconfig //更新库文件的缓存信息
(可以看到)/etc/ld.so.cache
4. log4cpp学习
http://blog.csdn.net/liuhong135541/article/category/1496383
sudo ldconfig 更新库文件的缓存信息
五、log4cpp的使用
(一)日志的格式: Layout
3种方式 | 打印的格式 |
---|---|
☆PatterLayout | 使用类似printf的格式化模式 |
BasicLayout | “以时间戳+优先级+内容” |
SimpleLayout | “优先级+日志信息” |
1. 涉及的头文件:
#include<log4cpp/BasicLayout.hh>
#include<log4cpp/SimpleLayout.hh>
#include<log4cpp/PatternLayout.hh>
2. 三种方式
(1)PatternLayout
构造函数:
log4cpp::PatternLayout::PatternLayout()
(1)PatterLayout 设置日志格式函数
void log4cpp::PatternLayout::setConversionPattern(const std::string &conversionPattern) throw
(ConfigureFailure)
(2)使用 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 %m%n");
日期%d 优先级[%p] 名字%c 消息(字符串)%m 换行%n
(2)BasicLayout
构造函数:
log4cpp::BasicLayout::BasicLayout()
输出例子格式:
1248337987 ERROR : Hello log4cppin a Error Message!
1248337987 WARN : Hello log4cppin a Warning Message!
(3)SimpleLayout
构造函数:
log4cpp::SimpleLayout::SimpleLayout()
(二)日志的目的地:Appender
1. 涉及头文件
#include <log4cpp/Appender.hh>
#include <log4cpp/OstreamAppender.hh>
#include <log4cpp/StringQueueAppender.hh>
#include <log4cpp/FileAppender.hh>
#include <log4cpp/RollingFileAppender.hh>
2. 四种常用方式
(1)OstreamAppender//输出到一个 ostream 类
(2)StringQueueAppender//内存队列
(3)FileAppender//写到文件中
(4)RollingFileAppender//文件过多之后,可以覆盖老文件
- RollingFileAppender回滚文件可以设置文件大小,如 5 * 1024
- 文件过多之后,可以覆盖老文件
RollingFileAppender("Appender的名字", "文件名", 文件大小, 几个文件)
(三)日志的记录:Category(负责写入信息)
1. 涉及头文件
#include<log4cpp/Category.hh>
2. 主要的成员函数
(1)设置Category的级别
void log4cpp::Category::setPriority (Priority::Value priority) throw
(2)设置或添加 Appender
void log4cpp::Category::addAppender (Appender *appender ) throw
(3)相应日志级别的记录
(emerg/fatal/alter/crit/error/warn/notice/info/debug/notset)
(四)日志的过滤:Priority (通过优先级实现)
1. 涉及头文件
#include<log4cpp/Priority.hh>
2. 优先级Priority的枚举类型
typedef enum
{
EMERG=0,
FATAL=0,
ALERT=100,
CRIT=200,
ERROR=300,
WARN=400,
NOTICE=500,
INFO=600,
DEBUG=700,
NOTSET =800
}PriorityLevel;
【注意】日志的优先级 ≥ Category记录的优先级
六、log4cpp应用例子
(一)写到屏幕上(用的OstreamAppender)
1 #include <iostream>
2 #include <log4cpp/PatternLayout.hh>
3 #include <log4cpp/OstreamAppender.hh>
4 #include <log4cpp/Category.hh>
5 #include <log4cpp/Priority.hh>
6
7 using std::cout;
8 using std::endl;
9
10 using namespace log4cpp;//引出log4cpp的命名空间
11
12 void test()
13 {
14 //日志的格式
15 PatternLayout *ppl = new PatternLayout();
16 ppl->setConversionPattern("%d %c [%p] %m%n");
17
18 //日志的目的地(会和格式Layout联系)
19 OstreamAppender *poa = new OstreamAppender("OstreamAppender123",&cout);
20 poa->setLayout(ppl);
21
22 //日志的记录器(会和目的地Appender联系)
23 Category &root = Category::getRoot();
24 root.addAppender(poa);
25
26 //日志的过滤器
27 root.setPriority(Priority::ERROR);//ERROR是优先级Priority的枚举类型之一
28
29 //用root调用相应的函数,这些函数都是记录器Category的成员函数
30 //日志的优先级 ≥ Category记录的优先级
31 root.emerg("This is an emerg message");
32 root.fatal("This is an fatal message");
33 root.alert("This is an alert message");
34 root.crit("This is an crit message");
35 root.error("This is an error message");
36 root.warn("This is an warn message");
37 root.info("This is an indo message");
38 root.debug("This is an debug message");
39
40 //空间的回收,使用写好的shutdown
41 Category::shutdown();
42 }
43
✹ 44 int main(int argc, char **argv)
45 {
46 test();
47 return 0;
48 }
(二)写到文件上(用的FileAppender)
1 #include <iostream>
2 #include <log4cpp/PatternLayout.hh>
3 #include <log4cpp/OstreamAppender.hh>
4 #include <log4cpp/FileAppender.hh>
5 #include <log4cpp/Category.hh>
6 #include <log4cpp/Priority.hh>
7
8 using std::cout;
9 using std::endl;
10
11 using namespace log4cpp;//引出log4cpp的命名空间
12
13 void test()
14 {
15 //日志的格式
16 PatternLayout *ppl = new PatternLayout();
17 ppl->setConversionPattern("%d %c [%p] %m%n");
18
19 //日志的目的地(会和格式Layout联系)
20 OstreamAppender *poa = new OstreamAppender("OstreamAppender123",&cout);
21 poa->setLayout(ppl);
22
23 FileAppender *pfa = new FileAppender("FileAppender1","wd.log");//Appender>
24 pfa->setLayout(ppl);
25
26 //日志的记录器(会和目的地Appender联系)
27 Category &root = Category::getRoot();
28 root.addAppender(poa);
29 root.addAppender(pfa);
30
31 //日志的过滤器
32 root.setPriority(Priority::ERROR);//ERROR是优先级Priority的枚举类型之一
33
34 //用root调用相应的函数,这些函数都是记录器Category的成员函数
35 //日志的优先级 ≥ Category记录的优先级
36 root.emerg("This is an emerg message");
37 root.fatal("This is an fatal message");
38 root.alert("This is an alert message");
39 root.crit("This is an crit message");
40 root.error("This is an error message");
41 root.warn("This is an warn message");
42 root.info("This is an indo message");
43 root.debug("This is an debug message");
44
45 //空间的回收,使用写好的shutdown
46 Category::shutdown();
47 }
48
✹ 49 int main(int argc, char **argv)
50 {
51 test();
52 return 0;
53 }
【bug】因为输出到屏幕和文件中了,所以其他地方没有问题,有问题的地方是空间回收shutdown
(1)错误原因:1个Layout对应了2个输出,回收的时候会double free
(2)改进:每1个Layout对应1个Appender
15 //日志的格式
16 PatternLayout *ppl1 = new PatternLayout();
17 ppl1->setConversionPattern("%d %c [%p] %m%n");
18
20 PatternLayout *ppl2 = new PatternLayout();
21 ppl2->setConversionPattern("%d %c [%p] %m%n");
22
23 //日志的目的地(会和格式Layout联系)
24 OstreamAppender *poa = new OstreamAppender("OstreamAppender123",&cout);
25 poa->setLayout(ppl1);
26
27 FileAppender *pfa = new FileAppender("FileAppender1","wd.log");//Appender>
28 pfa->setLayout(ppl2);
且发现了这种输出到文件的方式,是追加的
(三)FileAppender的改进型RollingFileAppender
1 #include <iostream>
2 #include <log4cpp/PatternLayout.hh>
3 #include <log4cpp/OstreamAppender.hh>
4 #include <log4cpp/RollingFileAppender.hh>
5 #include <log4cpp/Category.hh>
6 #include <log4cpp/Priority.hh>
7
8 using std::cout;
9 using std::endl;
10
11 using namespace log4cpp;//引出log4cpp的命名空间
12
13 void test()
14 {
15 //日志的格式
16 PatternLayout *ppl1 = new PatternLayout();
17 ppl1->setConversionPattern("%d %c [%p] %m%n");
18
19
20 PatternLayout *ppl2 = new PatternLayout();
21 ppl2->setConversionPattern("%d %c [%p] %m%n");
22
23 //日志的目的地(会和格式Layout联系)
24 OstreamAppender *poa = new OstreamAppender("OstreamAppender123",&cout);
25 poa->setLayout(ppl1);
26
27 RollingFileAppender *prfa = new RollingFileAppender("RollingFileAppender1",
28 "wd.log",
29 5 * 1024,
30 3);
31
32
33 prfa->setLayout(ppl2);
34
35 //日志的记录器(会和目的地Appender联系)
36 Category &root = Category::getRoot();
37 root.addAppender(poa);
38 root.addAppender(prfa);
39
40 //日志的过滤器
41 root.setPriority(Priority::ERROR);//ERROR是优先级Priority的枚举类型之一
42
43 //用root调用相应的函数,这些函数都是记录器Category的成员函数
44 //日志的优先级 ≥ Category记录的优先级
45 //∵内容不够多,可以循环输出,这样可以用到设置过的3个日志
46 size_t idx = 0;
47 while(idx < 100)
48 {
49 root.emerg("This is an emerg message");
50 root.fatal("This is an fatal message");
51 root.alert("This is an alert message");
52 root.crit("This is an crit message");
53 root.error("This is an error message");
54 root.warn("This is an warn message");
55 root.info("This is an indo message");
56 root.debug("This is an debug message");
57
58 ++idx;
59 }
60 //空间的回收,使用写好的shutdown
61 Category::shutdown();
62 }
63
✹ 64 int main(int argc, char **argv)
65 {
66 test();
67 return 0;
68 }
注意事项
- 用root调用相应的函数时,…代表可变参数
root.emerg(const char *format, ...);//...代表可变参数
//可变参数:参数的个数不确定,参数的类型不确定
如使用:printf("hello\n");
printf("%d\n", 10);
printf("%s %d %p\n", "hello", 10, &"hello");
- 编译的时候,注意:g++ xxx.cc -llog4cpp -lpthread
∵要依赖线程库
∴有-lpthread