log4cpp
是一个开源的 C++ 日志库,灵感来源于 Java 的 log4j
。它提供了灵活的日志记录功能,可以帮助开发者在 C++ 应用程序中记录、管理和格式化日志信息。log4cpp
支持多种日志记录策略和输出目标,可以满足各种不同的需求。
1.安装
下载压缩包
下载地址:https://sourceforge.net/projects/log4cpp/files/
安装步骤
$ tar xzvf log4cpp-1.1.4rc3.tar.gz
$ cd log4cpp
$ ./configure //进行自动化构建,自动生成makefile
$ make
$ sudo make install //安装 把头文件和库文件拷贝到系统路径下
//安装完后
//默认头文件路径:/usr/local/include/log4cpp
//默认lib库路径:/usr/local/lib
打开log4cpp官网:https://log4cpp.sourceforge.net/
拷贝simple example的内容,编译运行
#include "log4cpp/Category.hh" // 引入 log4cpp 库的 Category 类
#include "log4cpp/Appender.hh" // 引入 log4cpp 库的 Appender 基类
#include "log4cpp/FileAppender.hh" // 引入 log4cpp 库的 FileAppender 类,用于将日志写入文件
#include "log4cpp/OstreamAppender.hh" // 引入 log4cpp 库的 OstreamAppender 类,用于将日志输出到流(如 std::cout)
#include "log4cpp/Layout.hh" // 引入 log4cpp 库的 Layout 基类
#include "log4cpp/BasicLayout.hh" // 引入 log4cpp 库的 BasicLayout 类,用于简单的布局格式
#include "log4cpp/Priority.hh" // 引入 log4cpp 库的 Priority 类,用于设置日志优先级
int main(int argc, char** argv) {
// 创建一个 OstreamAppender 对象,日志输出到标准输出流(std::cout)
log4cpp::Appender *appender1 = new log4cpp::OstreamAppender("console", &std::cout);
appender1->setLayout(new log4cpp::BasicLayout()); // 设置日志布局为 BasicLayout
// 创建一个 FileAppender 对象,日志输出到文件 "program.log"
log4cpp::Appender *appender2 = new log4cpp::FileAppender("default", "program.log");
appender2->setLayout(new log4cpp::BasicLayout()); // 设置日志布局为 BasicLayout
// 获取根日志类别并设置其优先级为 WARN(警告)
log4cpp::Category& root = log4cpp::Category::getRoot();
root.setPriority(log4cpp::Priority::WARN);
root.addAppender(appender1); // 将 OstreamAppender 添加到根类别
// 获取名为 "sub1" 的日志类别,并将 FileAppender 添加到该类别
log4cpp::Category& sub1 = log4cpp::Category::getInstance(std::string("sub1"));
sub1.addAppender(appender2);
// 使用根类别记录错误和信息消息
root.error("root error"); // 记录错误级别的日志,输出到 std::cout
root.info("root info"); // 记录信息级别的日志,输出将被忽略(因根类别的优先级为 WARN)
// 使用 "sub1" 类别记录错误和警告消息
sub1.error("sub1 error"); // 记录错误级别的日志,输出到 "program.log"
sub1.warn("sub1 warn"); // 记录警告级别的日志,输出到 "program.log"
// 使用 printf 风格记录日志消息
root.warn("%d + %d == %s ?", 1, 1, "two"); // 格式化日志消息并记录到 std::cout
// 使用流的方式记录日志消息
root << log4cpp::Priority::ERROR << "Streamed root error"; // 记录错误级别的日志,输出到 std::cout
root << log4cpp::Priority::INFO << "Streamed root info"; // 记录信息级别的日志,输出将被忽略(因根类别的优先级为 WARN)
sub1 << log4cpp::Priority::ERROR << "Streamed sub1 error"; // 记录错误级别的日志,输出到 "program.log"
sub1 << log4cpp::Priority::WARN << "Streamed sub1 warn"; // 记录警告级别的日志,输出到 "program.log"
// 使用 errorStream() 记录错误级别的日志消息
root.errorStream() << "Another streamed error"; // 记录错误级别的日志,输出到 std::cout
return 0; // 程序结束
}
编译指令:** g++ log4cppTest.cc -llog4cpp -lpthread**
可能报错:找不到动态库
解决方法:
cd /etc
sudo vim ld.so.conf
将默认的lib库路径写入,再重新加载
sudo ldconfig
让动态链接库为系统所共享
ld.so.cache 执行了sudo ldconfig之后,会更新该缓存文件,会将所有动态库信息写入到该文件。当可执行程序需要加载相应动态库时,会从这里查找。
完成这些操作后,再使用上面的编译指令去编译示例代码
2.日志系统的设计
日志系统的设计,一般而言要抓住最核心的一条,就是日志从产生到到达最终目的地期间的处理流程。一般而言,为了设计一个灵活可扩展,可配置的日志库,主要将日志库分为4个部分去设计,分别是:记录器、过滤器、格式化器、输出器四部分。
记录器(日志来源):负责产生日志记录的原始信息,比如(原始信息,日志优先级,时间,记录的位置)等等信息。
过滤器(日志系统优先级):负责按指定的过滤条件过滤掉我们不需要的日志。
格式化器(日志布局):负责对原始日志信息按照我们想要的格式去格式化。
输出器(日志目的地):负责将将要进行记录的日志(一般经过过滤器及格式化器的处理后)记录到日志目的地(例如:输出到文件中)。
下面以一条日志的生命周期为例说明日志库是怎么工作的。
一条日志的生命周期:
-
产生:info(“log information.”);
-
经过记录器,记录器去获取日志发生的时间、位置、线程信息等等信息;
-
经过过滤器,决定是否记录;
-
经过格式化器处理成设定格式后传递给输出器。例如输出“2018-3-22 10:00:00 [info] log information.”这样格式的日志到文件中。日志的输出格式由格式化器实现,输出目的地则由输出器决定;
-
这条日志信息生命结束。
3.log4cpp的核心组件
3.1日志目的地(Appender)
Appender 是 log4cpp
的核心组件之一,负责将日志消息输出到不同的目标,如控制台、文件、网络等。log4cpp
提供了多种类型的 Appender,允许你将日志信息输出到多种地方。
- FileAppender:将日志消息写入文件。适合持久化日志记录。
- OstreamAppender :C++通用输出流(如 cout)
- ConsoleAppender:将日志消息输出到控制台。适用于调试和开发阶段。
- SyslogAppender:将日志消息发送到系统日志服务。
- RollingFileAppender:将日志写入文件,并支持日志文件的滚动,避免单个日志文件过大。
3.2日志布局(Layout)
Layout 定义了日志消息的格式。log4cpp
提供了不同类型的 Layout,用于格式化日志消息的输出内容。
- PatternLayout:最常用的布局,允许自定义格式化模式。例如,可以指定时间戳、日志级别、日志消息等。
- SimpleLayout:输出简洁的日志格式,通常只包含日志级别和消息。
- XMLLayout:将日志消息格式化为 XML 格式,适用于需要结构化日志数据的场景。
注意(极易出错):
当日志系统有多个日志目的地时,每一个目的地Appender都需要设置一个布局Layout(一对一关系)
3.3日志记录器(Category)
Category 是日志记录的核心组件,代表一个日志记录器。它管理不同的 Appender 和日志级别。Category
提供了多种方法来记录日志,如 debug
、info
、warn
、error
、fatal
。
- Root Category:系统默认的根日志记录器,所有日志记录都可以通过根记录器进行管理。
- 子类 Category:可以创建子类记录器,用于更细粒度的日志记录和管理。
创建Category对象时,可以用getRoot先创建root模块对象,对root模块对象设置优先级和目的地;
再用getInstance创建叶模块对象,叶模块对象会继承root模块对象的优先级和目的地,可以再去修改优先级、目的地
补充:如果没有创建根对象,直接使用getInstance创建叶对象,会先隐式地创建一个Root对象。
子Category可以继承父Category的信息:优先级、目的地
3.4日志优先级(Priority)
对于 log4cpp 而言,有两个优先级需要注意,一个是日志记录器的优先级,另一个就是某一条日志的优先级。Category对象就是日志记录器,在使用时须设置好其优先级;某一行日志的优先级,就是Category对象在调用某一个日志记录函数时指定的级别,如 logger.debug("this is a debug message") ,这一条日志的优先级就是DEBUG级别的。简言之:
日志系统有一个优先级A,日志信息有一个优先级B
只有B高于或等于A的时候,这条日志才会被输出(或保存),当B低于A的时候,这条日志会被过滤;
class LOG4CPP_EXPORT Priority {
public:
typedef enum {
EMERG = 0,
FATAL = 0,
ALERT = 100,
CRIT = 200,
ERROR = 300,
WARN = 400,
NOTICE = 500,
INFO = 600,
DEBUG = 700,
NOTSET = 800 //这个不代表可以使用的优先级
} PriorityLevel;
//......
}; //数值越小,优先级越高;数值越大,优先级越低
4.使用示例
4.1将日志输出到屏幕
#include <iostream>
#include <fstream>
#include <log4cpp/OstreamAppender.hh>
#include <log4cpp/PatternLayout.hh>
#include <log4cpp/Category.hh>
#include <log4cpp/Priority.hh>
using std::cout;
using std::endl;
using std::ofstream;
using namespace log4cpp;
void test0(){
//1.创建布局对象
PatternLayout * ptn1 = new PatternLayout();
ptn1->setConversionPattern("%d %c [%p] %m%n");
//2.创建目的地对象
OstreamAppender* appender1 = new OstreamAppender("output",&cout);
/* ofstream ofs("wd.log",std::ios::app); */
/* OstreamAppender* appender1 = new OstreamAppender("output",&ofs); */
//目的地绑定布局
appender1->setLayout(ptn1);
//3.创建记录器
/* Category & sub1 = Category::getRoot(); */
Category & sub1 = Category::getRoot().getInstance("sub1");
//4.设置系统的优先级
sub1.setPriority(Priority::WARN);
//5.记录器添加目的地
sub1.addAppender(appender1);
//6.写日志
sub1.emerg("this is an emerg msg");
sub1.fatal("this is a fatal msg");
sub1.alert("this is an alert msg");
sub1.crit("this is a crit msg");
sub1.error("this is an error msg");
sub1.warn("this is a warn msg");
sub1.notice("this is a notice msg");
sub1.info("this is an info msg");
sub1.debug("this is a debug msg");
//7.关闭资源
Category::shutdown();
}
int main(void){
test0();
return 0;
}
编译:g++ 01-log4cppPattern.cpp -llog4cpp -lpthread
4.2将日志输出到屏幕和保存到日志文件中
#include <iostream>
#include <log4cpp/OstreamAppender.hh>
#include <log4cpp/FileAppender.hh>
#include <log4cpp/PatternLayout.hh>
#include <log4cpp/Category.hh>
#include <log4cpp/Priority.hh>
using std::cout;
using std::endl;
using namespace log4cpp;
void test0(){
//1.创建布局对象
PatternLayout * ptn1 = new PatternLayout();
ptn1->setConversionPattern("%d %c [%p] %m%n");
PatternLayout * ptn2 = new PatternLayout();
ptn2->setConversionPattern("%d %c [%p] %m%n");
//2.创建目的地对象
OstreamAppender* appender1 = new OstreamAppender("output",&cout);
//目的地绑定布局
appender1->setLayout(ptn1);
FileAppender * appender2 = new FileAppender("fileApp","wd.log");
appender2->setLayout(ptn2);//目的地与布局一对一进行绑定
//3.创建记录器
/* Category & sub1 = Category::getRoot(); */
Category & sub1 = Category::getRoot().getInstance("sub1");
//4.设置系统的优先级
sub1.setPriority(Priority::WARN);
//5.记录器添加目的地
sub1.addAppender(appender1);
sub1.addAppender(appender2);
//6.写日志
sub1.emerg("this is an emerg msg");
sub1.fatal("this is a fatal msg");
sub1.alert("this is an alert msg");
sub1.crit("this is a crit msg");
sub1.error("this is an error msg");
sub1.warn("this is a warn msg");
sub1.notice("this is a notice msg");
sub1.info("this is an info msg");
sub1.debug("this is a debug msg");
//7.关闭资源
Category::shutdown();
//不要这样使用
/* delete ptn1; */
}
int main(void){
test0();
return 0;
}
4.3将日志输出保存到回滚文件
#include <iostream>
#include <log4cpp/OstreamAppender.hh>
#include <log4cpp/FileAppender.hh>
#include <log4cpp/RollingFileAppender.hh>
#include <log4cpp/PatternLayout.hh>
#include <log4cpp/Category.hh>
#include <log4cpp/Priority.hh>
using std::cout;
using std::endl;
using namespace log4cpp;
void test0(){
//1.创建布局对象
PatternLayout * ptn1 = new PatternLayout();
ptn1->setConversionPattern("%d %c [%p] %m%n");
PatternLayout * ptn2 = new PatternLayout();
ptn2->setConversionPattern("%d %c [%p] %m%n");
PatternLayout * ptn3 = new PatternLayout();
ptn3->setConversionPattern("%d %c [%p] %m%n");
//2.创建目的地对象
OstreamAppender* appender1 = new OstreamAppender("output",&cout);
//目的地绑定布局
appender1->setLayout(ptn1);
FileAppender * appender2 = new FileAppender("fileApp","wd.log");
appender2->setLayout(ptn2);//目的地与布局一对一进行绑定
auto appender3 = new RollingFileAppender("rolling",
"ROLL.log",
5 * 1024,
9);
appender3->setLayout(ptn3);
//3.创建记录器
/* Category & sub1 = Category::getRoot(); */
Category & sub1 = Category::getRoot().getInstance("sub1");
//4.设置系统的优先级
sub1.setPriority(Priority::DEBUG);
//5.记录器添加目的地
sub1.addAppender(appender1);
sub1.addAppender(appender2);
sub1.addAppender(appender3);
//6.写日志
int count = 0;
while(count < 30){
sub1.emerg("this is an emerg msg");
sub1.fatal("this is a fatal msg");
sub1.alert("this is an alert msg");
sub1.crit("this is a crit msg");
sub1.error("this is an error msg");
sub1.warn("this is a warn msg");
sub1.notice("this is a notice msg");
sub1.info("this is an info msg");
sub1.debug("this is a debug msg");
count++;
}
//7.关闭资源
Category::shutdown();
//不要这样使用
/* delete ptn1; */
}
int main(void){
test0();
return 0;
}
4.4log4cpp配置文件读取
#include <log4cpp/Category.hh> // 引入 log4cpp 库的 Category 类,用于日志记录
#include <log4cpp/PropertyConfigurator.hh> // 引入 log4cpp 库的 PropertyConfigurator 类,用于从配置文件中配置日志
int main(int argc, char* argv[])
{
// 指定配置文件名,通常是 log4cpp 的配置文件
std::string initFileName = "log4cpp.properties";
// 从配置文件中配置 log4cpp 的日志系统
log4cpp::PropertyConfigurator::configure(initFileName);
// 获取根日志类别
log4cpp::Category& root = log4cpp::Category::getRoot();
// 获取名为 "sub1" 的日志类别
log4cpp::Category& sub1 =
log4cpp::Category::getInstance(std::string("sub1"));
// 获取名为 "sub1.sub2" 的日志类别
log4cpp::Category& sub2 =
log4cpp::Category::getInstance(std::string("sub1.sub2"));
// 使用根类别记录警告消息
root.warn("Storm is coming");
// 使用 "sub1" 类别记录调试和信息消息
sub1.debug("Received storm warning");
sub1.info("Closing all hatches");
// 使用 "sub1.sub2" 类别记录调试和错误消息
sub2.debug("Hiding solar panels");
sub2.error("Solar panels are blocked");
sub2.debug("Applying protective shield");
sub2.warn("Unfolding protective shield");
sub2.info("Solar panels are shielded");
// 使用 "sub1" 类别记录信息消息
sub1.info("All hatches closed");
// 使用根类别记录信息消息
root.info("Ready for storm.");
// 关闭 log4cpp 日志系统
log4cpp::Category::shutdown();
return 0; // 程序结束
}
//log4cpp.properties
log4cpp.rootCategory=DEBUG, rootAppender
log4cpp.category.sub1=DEBUG, A1, A2
log4cpp.category.sub1.sub2=DEBUG, A3
log4cpp.appender.rootAppender=ConsoleAppender
log4cpp.appender.rootAppender.layout=PatternLayout
log4cpp.appender.rootAppender.layout.ConversionPattern=%d [%p] %m%n
log4cpp.appender.A1=FileAppender
log4cpp.appender.A1.fileName=A1.log
log4cpp.appender.A1.layout=BasicLayout
log4cpp.appender.A2=FileAppender
log4cpp.appender.A2.threshold=WARN
log4cpp.appender.A2.fileName=A2.log
log4cpp.appender.A2.layout=PatternLayout
log4cpp.appender.A2.layout.ConversionPattern=%d [%p] %m%n
log4cpp.appender.A3=RollingFileAppender
log4cpp.appender.A3.fileName=A3.log
log4cpp.appender.A3.maxFileSize=200
log4cpp.appender.A3.maxBackupIndex=1
log4cpp.appender.A3.layout=PatternLayout
log4cpp.appender.A3.layout.ConversionPattern=%d [%p] %m%n