news 2026/10/5 4:13:16

C++ 日志库log4cpp使用详解

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
C++ 日志库log4cpp使用详解

log4cpp 是一个基于 C++ 的开源日志库,灵感来源于 Java 的 log4j,提供了灵活的日志管理功能,包括日志级别控制、多种输出目的地、日志格式自定义等。它特别适合中大型 C++ 项目,能够满足复杂的日志需求。本文将详细介绍 log4cpp 的核心概念、使用方法和最佳实践。

一、log4cpp 核心概念

在使用 log4cpp 之前,需要了解几个核心组件:

  • Category(日志类别):用于区分不同模块的日志,类似日志的"命名空间",可以通过层次结构组织(如root.category、root.category.net)。
  • Appender(输出器):定义日志的输出目的地,如控制台、文件、网络等,一个 Category 可以关联多个 Appender。
  • Layout(布局):控制日志的输出格式,如是否包含时间、日志级别、文件名、行号等信息。
  • Priority(优先级):日志级别,从高到低为:FATAL、ALERT、CRIT、ERROR、WARN、NOTICE、INFO、DEBUG。

二、安装 log4cpp

1. Linux 系统安装

1

2

3

4

5

# Ubuntu/Debian

sudoapt-getinstallliblog4cpp5-dev

# CentOS/RHEL(需先配置 EPEL 源)

sudoyuminstalllog4cpp-devel

2. 源码编译安装

1

2

3

4

5

6

7

8

9

# 下载源码

wget https://downloads.sourceforge.net/project/log4cpp/log4cpp-1.1.x%20%28new%29/log4cpp-1.1.3/log4cpp-1.1.3.tar.gz

tarzxvf log4cpp-1.1.3.tar.gz

cdlog4cpp-1.1.3

# 编译安装

./configure--prefix=/usr/local

make

sudomakeinstall

三、log4cpp 基本使用示例

下面是一个包含函数名、行号等信息的 log4cpp 入门示例:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

66

67

68

69

70

71

72

73

74

75

76

77

78

79

80

81

82

83

84

85

86

87

88

89

90

91

92

93

94

95

96

97

98

#include <log4cpp/Category.hh>

#include <log4cpp/PropertyConfigurator.hh>

#include <log4cpp/Appender.hh>

#include <log4cpp/FileAppender.hh>

#include <log4cpp/OstreamAppender.hh>

#include <log4cpp/Layout.hh>

#include <log4cpp/PatternLayout.hh>

#include <log4cpp/Priority.hh>

// 定义日志宏,自动添加文件名、函数名和行号

#define LOG_DEBUG(msg) \

log4cpp::Category::getRoot() << log4cpp::Priority::DEBUG <<"["<< __FILE__ <<":"<< __LINE__ <<"] "<< __func__ <<": "<< msg

#define LOG_INFO(msg) \

log4cpp::Category::getRoot() << log4cpp::Priority::INFO <<"["<< __FILE__ <<":"<< __LINE__ <<"] "<< __func__ <<": "<< msg

#define LOG_WARN(msg) \

log4cpp::Category::getRoot() << log4cpp::Priority::WARN <<"["<< __FILE__ <<":"<< __LINE__ <<"] "<< __func__ <<": "<< msg

#define LOG_ERROR(msg) \

log4cpp::Category::getRoot() << log4cpp::Priority::ERROR <<"["<< __FILE__ <<":"<< __LINE__ <<"] "<< __func__ <<": "<< msg

#define LOG_FATAL(msg) \

log4cpp::Category::getRoot() << log4cpp::Priority::FATAL <<"["<< __FILE__ <<":"<< __LINE__ <<"] "<< __func__ <<": "<< msg

// 带格式化输出的日志宏

#define LOG_DEBUG_F(fmt, ...) \

do{ \

charbuffer[1024]; \

snprintf(buffer,sizeof(buffer), fmt, ##__VA_ARGS__); \

LOG_DEBUG(buffer); \

}while(0)

#define LOG_INFO_F(fmt, ...) \

do{ \

charbuffer[1024]; \

snprintf(buffer,sizeof(buffer), fmt, ##__VA_ARGS__); \

LOG_INFO(buffer); \

}while(0)

// 初始化日志配置

voidinitLog() {

// 创建控制台输出器

log4cpp::Appender* consoleAppender =newlog4cpp::OstreamAppender("console", &std::cout);

// 创建文件输出器

log4cpp::Appender* fileAppender =newlog4cpp::FileAppender("file","app.log");

// 创建布局,设置日志格式

log4cpp::PatternLayout* layout =newlog4cpp::PatternLayout();

// 格式说明:%d-日期 %p-优先级 %c-类别 %m-消息 %n-换行

layout->setConversionPattern("%d{%Y-%m-%d %H:%M:%S} %p %c %m%n");

// 为输出器设置布局

consoleAppender->setLayout(layout);

fileAppender->setLayout(layout);

// 获取根日志类别,并添加输出器

log4cpp::Category& root = log4cpp::Category::getRoot();

root.addAppender(consoleAppender);

root.addAppender(fileAppender);

// 设置根日志的优先级(低于此级别的日志不输出)

root.setPriority(log4cpp::Priority::DEBUG);

}

// 测试函数

voidtestLogFunction(intvalue) {

LOG_DEBUG("进入测试函数");

LOG_INFO_F("参数值为: %d", value);

if(value < 0) {

LOG_WARN("参数值为负数");

}

if(value == 0) {

LOG_ERROR("参数值不能为零");

}

}

intmain() {

// 初始化日志

initLog();

LOG_INFO("程序启动");

// 测试日志输出

testLogFunction(10);

testLogFunction(-5);

testLogFunction(0);

LOG_INFO("程序退出");

// 清理日志资源

log4cpp::Category::shutdown();

return0;

}

编译与运行

编译时需要链接 log4cpp 库:

1

2

g++ log4cpp_demo.cpp -o log_demo -llog4cpp -lpthread

./log_demo

运行后会生成app.log文件,同时在控制台输出日志,典型输出如下:

2024-09-15 10:30:00 INFO root [log4cpp_demo.cpp:66] main: 程序启动
2024-09-15 10:30:00 DEBUG root [log4cpp_demo.cpp:59] testLogFunction: 进入测试函数
2024-09-15 10:30:00 INFO root [log4cpp_demo.cpp:60] testLogFunction: 参数值为: 10
2024-09-15 10:30:00 DEBUG root [log4cpp_demo.cpp:59] testLogFunction: 进入测试函数
2024-09-15 10:30:00 INFO root [log4cpp_demo.cpp:60] testLogFunction: 参数值为: -5
2024-09-15 10:30:00 WARN root [log4cpp_demo.cpp:62] testLogFunction: 参数值为负数
2024-09-15 10:30:00 DEBUG root [log4cpp_demo.cpp:59] testLogFunction: 进入测试函数
2024-09-15 10:30:00 INFO root [log4cpp_demo.cpp:60] testLogFunction: 参数值为: 0
2024-09-15 10:30:00 ERROR root [log4cpp_demo.cpp:65] testLogFunction: 参数值不能为零
2024-09-15 10:30:00 INFO root [log4cpp_demo.cpp:70] main: 程序退出

四、通过配置文件使用 log4cpp

对于复杂项目,推荐使用配置文件来管理 log4cpp 配置,这样可以在不修改代码的情况下调整日志行为。

1. 配置文件(log4cpp.properties)

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

# 根日志类别设置

log4cpp.rootCategory=DEBUG, console, file

# 控制台输出器配置

log4cpp.appender.console=org.apache.log4j.ConsoleAppender

log4cpp.appender.console.layout=org.apache.log4j.PatternLayout

log4cpp.appender.console.layout.ConversionPattern=%d{%Y-%m-%d %H:%M:%S} %p %c %m%n

# 文件输出器配置

log4cpp.appender.file=org.apache.log4j.FileAppender

log4cpp.appender.file.fileName=app.log

log4cpp.appender.file.layout=org.apache.log4j.PatternLayout

log4cpp.appender.file.layout.ConversionPattern=%d{%Y-%m-%d %H:%M:%S} %p %c [%-20F:%-4L] %-20M %m%n

# 模块特定日志配置(例如网络模块)

log4cpp.category.net=INFO, netfile

log4cpp.additivity.net=false

log4cpp.appender.netfile=org.apache.log4j.FileAppender

log4cpp.appender.netfile.fileName=net.log

log4cpp.appender.netfile.layout=org.apache.log4j.PatternLayout

log4cpp.appender.netfile.layout.ConversionPattern=%d %p %m%n

2. 使用配置文件的代码

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

#include <log4cpp/Category.hh>

#include <log4cpp/PropertyConfigurator.hh>

// 日志宏定义(同上)

// ...

intmain() {

try{

// 通过配置文件初始化

log4cpp::PropertyConfigurator::configure("log4cpp.properties");

}catch(log4cpp::ConfigureFailure& f) {

std::cerr <<"配置文件加载失败: "<< f.what() << std::endl;

return1;

}

// 获取根日志类别

log4cpp::Category& root = log4cpp::Category::getRoot();

root.info("程序启动");

// 获取网络模块日志类别

log4cpp::Category& netCat = log4cpp::Category::getInstance("net");

netCat.info("网络连接建立");

// ... 其他日志输出

log4cpp::Category::shutdown();

return0;

}

五、log4cpp 高级特性

1. 日志滚动(RollingFileAppender)

当日志文件达到一定大小后自动创建新文件:

1

2

3

// 在代码中配置

log4cpp::RollingFileAppender* rollingAppender =

newlog4cpp::RollingFileAppender("rolling","rolling.log", 1024*1024, 5);// 1MB 每个,最多 5 个文件

或在配置文件中:

1

2

3

4

log4cpp.appender.rolling=org.apache.log4j.RollingFileAppender

log4cpp.appender.rolling.fileName=rolling.log

log4cpp.appender.rolling.maxFileSize=1048576

log4cpp.appender.rolling.maxBackupIndex=5

2. 每日滚动日志(DailyRollingFileAppender)

按时间自动切割日志(如每天生成一个新日志文件):

1

2

3

log4cpp.appender.daily=org.apache.log4j.DailyRollingFileAppender

log4cpp.appender.daily.fileName=daily.log

log4cpp.appender.daily.datePattern='.'yyyy-MM-dd

3. 日志级别控制

可以为不同的日志类别设置不同的级别,例如开发环境输出 DEBUG 级别,生产环境只输出 INFO 及以上级别:

1

2

3

// 代码中设置

log4cpp::Category::getRoot().setPriority(log4cpp::Priority::INFO);

log4cpp::Category::getInstance("debug.module").setPriority(log4cpp::Priority::DEBUG);

六、最佳实践

  1. 使用宏封装日志调用:如示例中定义的LOG_DEBUG、LOG_INFO等宏,自动添加文件名、函数名和行号信息。

  2. 合理设置日志级别:

    • DEBUG:开发调试信息,生产环境通常关闭
    • INFO:正常运行状态信息
    • WARN:需要注意的异常情况,但不影响程序运行
    • ERROR:错误信息,可能影响部分功能
    • FATAL:致命错误,程序可能无法继续运行
  3. 按模块划分日志类别:如net、db、ui等,便于日志过滤和分析。

  4. 避免在日志中输出敏感信息:如密码、个人信息等。

  5. 注意日志性能:

    • 避免在高频调用的函数中输出大量 DEBUG 日志
    • 复杂格式化操作可以先判断日志级别再执行
  6. 及时清理日志:配置日志滚动或定时清理机制,避免磁盘空间耗尽。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/10/5 4:11:24

Backtrader零基础入门:从双均线策略到实盘级回测

1. 为什么Backtrader是量化新手最该踩实的第一块砖我带过不少想入行量化的朋友&#xff0c;从金融专业毕业生到转行的程序员&#xff0c;甚至还有做了十年实体生意突然想试试“用代码赚钱”的老板。他们问得最多的问题不是“怎么选因子”&#xff0c;而是&#xff1a;“我连K线…

作者头像 李华
网站建设 2026/10/5 4:10:12

插件系统开发指南:从plugin.json配置到TypeScript SDK实战

1. 从“plugins”这个标题说起&#xff1a;插件系统到底在解决什么问题“plugins”这个词看起来简单&#xff0c;但它背后牵扯的东西其实非常多。如果你是在搜索框里敲下这个词&#xff0c;大概率你正在面对下面几种情况之一&#xff1a;你下载了一个工具&#xff0c;发现它支持…

作者头像 李华
网站建设 2026/10/5 4:09:46

3D角色资源制作规范:面数、UV、贴图与MaxScript检查全解析

简介&#xff1a;这份《3D角色资源制作规范》文档以《死神》项目为案例&#xff0c;面向游戏或动画行业的3D建模师、技术美术及项目管理者&#xff0c;提供一套从模型创建到后期优化全流程的标准化操作指引。资源为1个docx文件&#xff0c;共4.89MB&#xff0c;内容覆盖模型三角…

作者头像 李华
网站建设 2026/10/5 4:09:25

SpringBoot+Vue文学创作社交论坛系统:架构设计与源码解析

做文学社区类产品&#xff0c;最头疼的往往不是某个功能写不出来&#xff0c;而是“创作、连载、评论、论坛”这些东西凑到一起之后&#xff0c;数据模型、权限边界、状态流转全搅在一起&#xff0c;代码越写越乱。我去年集中调研过一批现成的管理系统源码&#xff0c;前前后后…

作者头像 李华
网站建设 2026/10/5 4:08:48

多周期路径约束实战指南:从原理到时序收敛

1. 多周期路径&#xff1a;什么时候需要&#xff0c;什么时候不需要做时序收敛的工程师&#xff0c;几乎都有过被set_multicycle_path折腾到怀疑人生的经历。明明功能仿真完全正确&#xff0c;上板就是跑不起来&#xff1b;明明时钟频率压得很低&#xff0c;时序报告里偏偏有红…

作者头像 李华
网站建设 2026/10/5 4:08:43

Python堆与heapq:TopK、优先队列与内存优化实战

前阵子帮一个做日志分析的同事改代码&#xff0c;他那段程序要从每天上亿条请求日志里捞出响应时间最长的100条。第一版实现特别直白&#xff1a;全量解析完排个序&#xff0c;再切片取前100。结果呢&#xff1f;近一亿条记录解析完直接吃掉16G内存&#xff0c;光排序就跑了40多…

作者头像 李华