C++ 日志库log4cpp使用详解

 更新时间:2026年01月29日 10:02:36   作者:bkspiderx  
log4cpp是基于log4j设计的开源日志库,包括日志级别控制、多种输出目的地、日志格式自定义等,本文介绍了C++日志库log4cpp的核心概念与使用方法,感兴趣的可以了解一下

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

一、log4cpp 核心概念

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

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

二、安装 log4cpp

1. Linux 系统安装

# Ubuntu/Debian
sudo apt-get install liblog4cpp5-dev

# CentOS/RHEL(需先配置 EPEL 源)
sudo yum install log4cpp-devel

2. 源码编译安装

# 下载源码
wget https://downloads.sourceforge.net/project/log4cpp/log4cpp-1.1.x%20%28new%29/log4cpp-1.1.3/log4cpp-1.1.3.tar.gz
tar zxvf log4cpp-1.1.3.tar.gz
cd log4cpp-1.1.3

# 编译安装
./configure --prefix=/usr/local
make
sudo make install

三、log4cpp 基本使用示例

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

#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 { \
        char buffer[1024]; \
        snprintf(buffer, sizeof(buffer), fmt, ##__VA_ARGS__); \
        LOG_DEBUG(buffer); \
    } while(0)

#define LOG_INFO_F(fmt, ...) \
    do { \
        char buffer[1024]; \
        snprintf(buffer, sizeof(buffer), fmt, ##__VA_ARGS__); \
        LOG_INFO(buffer); \
    } while(0)

// 初始化日志配置
void initLog() {
    // 创建控制台输出器
    log4cpp::Appender* consoleAppender = new log4cpp::OstreamAppender("console", &std::cout);
    
    // 创建文件输出器
    log4cpp::Appender* fileAppender = new log4cpp::FileAppender("file", "app.log");
    
    // 创建布局,设置日志格式
    log4cpp::PatternLayout* layout = new log4cpp::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);
}

// 测试函数
void testLogFunction(int value) {
    LOG_DEBUG("进入测试函数");
    LOG_INFO_F("参数值为: %d", value);
    
    if (value < 0) {
        LOG_WARN("参数值为负数");
    }
    
    if (value == 0) {
        LOG_ERROR("参数值不能为零");
    }
}

int main() {
    // 初始化日志
    initLog();
    
    LOG_INFO("程序启动");
    
    // 测试日志输出
    testLogFunction(10);
    testLogFunction(-5);
    testLogFunction(0);
    
    LOG_INFO("程序退出");
    
    // 清理日志资源
    log4cpp::Category::shutdown();
    return 0;
}
    

编译与运行

编译时需要链接 log4cpp 库:

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)

# 根日志类别设置
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. 使用配置文件的代码

#include <log4cpp/Category.hh>
#include <log4cpp/PropertyConfigurator.hh>

// 日志宏定义(同上)
// ...

int main() {
    try {
        // 通过配置文件初始化
        log4cpp::PropertyConfigurator::configure("log4cpp.properties");
    } catch (log4cpp::ConfigureFailure& f) {
        std::cerr << "配置文件加载失败: " << f.what() << std::endl;
        return 1;
    }
    
    // 获取根日志类别
    log4cpp::Category& root = log4cpp::Category::getRoot();
    root.info("程序启动");
    
    // 获取网络模块日志类别
    log4cpp::Category& netCat = log4cpp::Category::getInstance("net");
    netCat.info("网络连接建立");
    
    // ... 其他日志输出
    
    log4cpp::Category::shutdown();
    return 0;
}

五、log4cpp 高级特性

1. 日志滚动(RollingFileAppender)

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

// 在代码中配置
log4cpp::RollingFileAppender* rollingAppender = 
    new log4cpp::RollingFileAppender("rolling", "rolling.log", 1024*1024, 5); // 1MB 每个,最多 5 个文件

或在配置文件中:

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)

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

log4cpp.appender.daily=org.apache.log4j.DailyRollingFileAppender
log4cpp.appender.daily.fileName=daily.log
log4cpp.appender.daily.datePattern='.'yyyy-MM-dd

3. 日志级别控制

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

// 代码中设置
log4cpp::Category::getRoot().setPriority(log4cpp::Priority::INFO);
log4cpp::Category::getInstance("debug.module").setPriority(log4cpp::Priority::DEBUG);

六、最佳实践

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

  2. 合理设置日志级别

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

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

  5. 注意日志性能

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

七、总结

log4cpp 提供了灵活而强大的日志功能,通过合理配置可以满足各种复杂的日志需求。它的核心优势在于:

  • 支持多种日志输出目的地(控制台、文件、网络等)
  • 可自定义日志格式,轻松添加函数名、行号等上下文信息
  • 支持日志级别控制,便于在不同环境下调整日志详细程度
  • 支持日志滚动,方便日志管理和归档

掌握 log4cpp 的使用,能够显著提升 C++ 项目的调试效率和运维能力。在实际项目中,建议结合配置文件使用,以便在不修改代码的情况下灵活调整日志策略。

到此这篇关于C++ 日志库log4cpp使用详解的文章就介绍到这了,更多相关C++ 日志库log4cpp内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

您可能感兴趣的文章:

相关文章

  • 深入理解strcpy与memcpy的区别

    深入理解strcpy与memcpy的区别

    本篇文章是对strcpy与memcpy的区别进行了详细的分析介绍,需要的朋友参考下
    2013-05-05
  • C++与QML交互的项目实践

    C++与QML交互的项目实践

    本文主要介绍了C++与QML交互的项目实践,将详细介绍C++与QML的交互方式,包括在QML中调用C++函数和在C++中访问QML元素,具有一定的参考价值,感兴趣的可以了解一下
    2023-09-09
  • C语言代码中调用C++代码的方法示例

    C语言代码中调用C++代码的方法示例

    这篇文章主要介绍了C语言代码中调用C++代码的方法示例,文中也介绍了C++代码调用C代码的方法,有需要的朋友可以参考借鉴,下面来一起看看吧。
    2017-02-02
  • C++ OpenCV实战之车道检测

    C++ OpenCV实战之车道检测

    这篇文章主要介绍了基于C++ OpenCV实现的车道检测,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2022-01-01
  • Linux页面置换算法的C语言实现

    Linux页面置换算法的C语言实现

    这篇文章主要为大家详细介绍了Linux页面置换算法的C语言实现,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2020-12-12
  • C/C++实现控制台输出不同颜色字体的方法

    C/C++实现控制台输出不同颜色字体的方法

    这篇文章主要介绍了C/C++实现控制台输出不同颜色字体的方法,涉及C++控制台文字属性相关设置操作技巧,需要的朋友可以参考下
    2017-09-09
  • C++静态持续变量介绍

    C++静态持续变量介绍

    这篇文章主要介绍了 C++静态持续变量,静态持续变量的定义C++和C语言是一样的,它拥有三种链接性,即外部链接性、内部连接性和无链接性。其中外部链接性指的是可以在其他文件中访问,内部链接性指的是只能在当前文件访问,需要的朋友可以参考一下
    2021-11-11
  • C++ string格式化输出方式

    C++ string格式化输出方式

    今天小编就为大家分享一篇C++ string格式化输出方式,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧
    2019-12-12
  • Visual C++ 常用数据类型转换方法详解

    Visual C++ 常用数据类型转换方法详解

    本文纯粹是总结一下有关类型转换的贴子,需要的朋友可以参考下
    2017-06-06
  • c语言中&的用法示例代码

    c语言中&的用法示例代码

    这篇文章主要给大家介绍了关于c语言中&的用法的相关资料,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2020-09-09

最新评论