【Linux C++】log4cpp日志库的安装和使用详解
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::coutroot.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::coutroot << 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::coutreturn 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, A3log4cpp.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=BasicLayoutlog4cpp.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