c++日志库-log4cplus

《log4cplus日志库》

1. Preface

  log4cplus是一款开源的c++日志库,具有线程安全,灵活,以及多粒度控制的特点;log4cplus可以将日志按照优先级进行划分,使其可以面向程序的调试,运行,测试,后期维护等软件全生命周期;可以通过配置,选择将日志输出到屏幕,文件,NT event log ,甚至是远程服务器。可通过配置控制日志的输出格式,也可通过指定策略对日志进行定期备份。

2. 源码下载编译

2.1 源码下载

log4cplus源码的下载可访问:log4cplus源码下载

2.2 编译

  由于只在Linux和Windows两个平台使用c++,对cmake文件做一些改动;log4clpus的主要源码位于目录src,以及include中,docs目录中为相关说明文档;

log4cplus根目录下CMakeLists.txt文件内容:

project (log4cplus)
cmake_minimum_required (VERSION 3.10)

# 编译器使用-fPIC选项,生成位置无关的代码
# 如果代码需要生成动态库供别人使用,建议增加此选项
# Use "-fPIC" / "-fPIE" for all targets by default, including static libs.
# https://blog.csdn.net/zhizhengguan/article/details/115323750
set (CMAKE_POSITION_INDEPENDENT_CODE ON)

# 针对MSVC编译器 进行设置 目标平台
if (MSVC)
  #set (CMAKE_CXX_STANDARD 14) ARM平台
  if (CMAKE_VS_PLATFORM_NAME MATCHES "ARM")
    set (CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /EHsc")
  endif ()
endif ()

# set_target_properties
# https://blog.csdn.net/weixin_39766005/article/details/122481172
# set (CXX_EXTENSIONS ON)     # 全局设置
#set_target_properties(target PROPERTIES
#  CXX_STANDARD 11
#  CXX_STANDARD_REQUIRED ON
#  CXX_EXTENSIONS ON
#)

# GUN安装目录
if(UNIX)
  include(GNUInstallDirs)
endif(UNIX)

# include 包含文件  log4cplus_get_version()是定义的一个宏(类似函数)的东西
include (Log4CPlusUtils.cmake)
log4cplus_get_version ("${PROJECT_SOURCE_DIR}/include"
  log4cplus_version_major log4cplus_version_minor log4cplus_version_patch)
message("-- Generating build for Log4cplus version ${log4cplus_version_major}.${log4cplus_version_minor}.${log4cplus_version_patch}")

set (log4cplus_soversion 3)
set (log4cplus_postfix "")

# option选项
option(LOG4CPLUS_BUILD_TESTING "Build the test suite." OFF)
option(LOG4CPLUS_BUILD_LOGGINGSERVER "Build the logging server." ON)

# 具体看代码中宏LOG4CPLUS_REQUIRE_EXPLICIT_INITIALIZATION定义
option(LOG4CPLUS_REQUIRE_EXPLICIT_INITIALIZATION "Require explicit initialization (see log4cplus::Initializer)" OFF)
if (LOG4CPLUS_REQUIRE_EXPLICIT_INITIALIZATION)
  add_compile_definitions (LOG4CPLUS_REQUIRE_EXPLICIT_INITIALIZATION=1)
endif(LOG4CPLUS_REQUIRE_EXPLICIT_INITIALIZATION)

# add_compile_definitions: 添加宏定义  线程池使能
option(LOG4CPLUS_ENABLE_THREAD_POOL "Instantiate internal thread pool for when AsyncAppend=true" ON)
if (LOG4CPLUS_ENABLE_THREAD_POOL)
  add_compile_definitions (LOG4CPLUS_ENABLE_THREAD_POOL=1)
endif()

# single thread 单线程开启
if(NOT LOG4CPLUS_SINGLE_THREADED)
  find_package (Threads)
  message (STATUS "Threads: ${CMAKE_THREAD_LIBS_INIT}")
endif(NOT LOG4CPLUS_SINGLE_THREADED)

if(LOG4CPLUS_SINGLE_THREADED AND LOG4CPLUS_BUILD_LOGGINGSERVER)
  message (FATAL_ERROR "The logging server requires the multi-thread capability.")
endif(LOG4CPLUS_SINGLE_THREADED AND LOG4CPLUS_BUILD_LOGGINGSERVER)

set(BUILD_SHARED_LIBS TRUE CACHE BOOL "If TRUE, log4cplus is built as a shared library, otherwise as a static library")

if (WIN32)
  set (UNICODE_DEFAULT OFF)     # 修改位OFF
else (WIN32)
  set (UNICODE_DEFAULT OFF)
endif (WIN32)

if (MSVC)
  set (LOG4CPLUS_WORKING_LOCALE_DEFAULT ON)
else (MSVC)
  set (LOG4CPLUS_WORKING_LOCALE_DEFAULT OFF)
endif (MSVC)

option(LOG4CPLUS_WORKING_LOCALE "Define for compilers/standard libraries that support more than just the C locale."
  ${LOG4CPLUS_WORKING_LOCALE_DEFAULT})

option(LOG4CPLUS_WORKING_C_LOCALE
  "Define for compilers/standard libraries that have working C locale."
  OFF)

option(LOG4CPLUS_SINGLE_THREADED
  "Define if you want to build single-threaded version of the library."
  OFF)
  
if (LOG4CPLUS_SINGLE_THREADED)
  add_compile_definitions (LOG4CPLUS_SINGLE_THREADED=1)
endif (LOG4CPLUS_SINGLE_THREADED)

option(UNICODE "Build with tchar = wchar_t" ${UNICODE_DEFAULT})

option(WITH_ICONV "Use iconv() for char->wchar_t conversion."
  OFF)

option(ENABLE_SYMBOLS_VISIBILITY
  "Enable compiler and platform specific options for symbols visibility"
  ON)

if (MINGW)
  option(LOG4CPLUS_MINGW_STATIC_RUNTIME "Enable MinGW static runtime" OFF)
endif()

# WITH_UNIT_TESTS 修改位OFF
option(WITH_UNIT_TESTS "Enable unit tests" OFF)
if (WITH_UNIT_TESTS)
  set (LOG4CPLUS_WITH_UNIT_TESTS 1)
  add_compile_definitions (CATCH_CONFIG_PREFIX_ALL=1)
  if (WIN32)
    add_compile_definitions (LOG4CPLUS_WITH_UNIT_TESTS=1)
  endif (WIN32)
endif (WITH_UNIT_TESTS)

# set(_WIN32_WINNT 0x0600 CACHE STRING "Define Windows API version to use.")

option(LOG4CPLUS_ENABLE_DECORATED_LIBRARY_NAME
  "Turns on resulting file name decoration for static and UNICODE builds." OFF)
if (LOG4CPLUS_ENABLE_DECORATED_LIBRARY_NAME)
  if (NOT ${BUILD_SHARED_LIBS})
    # set S-prefix for static build
    set (log4cplus_postfix "${log4cplus_postfix}S")
  endif ()

  if (UNICODE)
    set (log4cplus_postfix "${log4cplus_postfix}U")
  endif (UNICODE)
endif (LOG4CPLUS_ENABLE_DECORATED_LIBRARY_NAME)

set (log4cplus "log4cplus${log4cplus_postfix}")

if (WITH_ICONV)
  set(LOG4CPLUS_WITH_ICONV 1)
endif ()

# 执行检查脚本 先检查是否有自定义脚本,如果没有,则将其设置位log4cplus提供的ConfigureChecks.cmake脚本,然后include
if(LOG4CPLUS_CONFIGURE_CHECKS_PATH)
  get_filename_component(LOG4CPLUS_CONFIGURE_CHECKS_PATH "${LOG4CPLUS_CONFIGURE_CHECKS_PATH}" ABSOLUTE)
endif()

if(LOG4CPLUS_CONFIGURE_CHECKS_PATH AND EXISTS ${LOG4CPLUS_CONFIGURE_CHECKS_PATH})
  message(STATUS "Custom ConfigureChecks script found: ${LOG4CPLUS_CONFIGURE_CHECKS_PATH}")
else()
  set(LOG4CPLUS_CONFIGURE_CHECKS_PATH "ConfigureChecks.cmake")
endif()
# include 检查脚本,include具体做了什么?
include(${LOG4CPLUS_CONFIGURE_CHECKS_PATH})

# Prepare defines.hxx.cmake from defines.hxx.in by doing a bit of
# string manipulation.
file(READ include/log4cplus/config/defines.hxx.in FILE_BUFFER)
string(REGEX REPLACE "#undef[^a-zA-Z0-9_]+([a-zA-Z0-9_]+)"
  "#cmakedefine \\1 @\\1@" FILE_BUFFER "${FILE_BUFFER}")
set(DEFINES_HXX_CMAKE
  ${log4cplus_BINARY_DIR}/include/log4cplus/config/defines.hxx.cmake)
set(DEFINES_HXX ${log4cplus_BINARY_DIR}/include/log4cplus/config/defines.hxx)
file(WRITE ${DEFINES_HXX_CMAKE} ${FILE_BUFFER})

configure_file(${DEFINES_HXX_CMAKE} ${DEFINES_HXX} @ONLY)

# 包含头文件
include_directories (${log4cplus_SOURCE_DIR}/include
                     ${log4cplus_SOURCE_DIR}/threadpool
                     ${log4cplus_SOURCE_DIR}/catch/single_include/catch2
                     ${log4cplus_BINARY_DIR}/include
                    )

# 已经全局设置
# set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${PROJECT_BINARY_DIR}/bin)

if (LOG4CPLUS_BUILD_TESTING)
  enable_testing()
endif (LOG4CPLUS_BUILD_TESTING)

# 构建
add_subdirectory (src)

# If the CMake version supports it, attach header directory information
# to the targets for when we are part of a parent build (ie being pulled
# in via add_subdirectory() rather than being a standalone build).
if (DEFINED CMAKE_VERSION AND NOT "${CMAKE_VERSION}" VERSION_LESS "2.8.11")
  target_include_directories(${log4cplus} INTERFACE
    $<BUILD_INTERFACE:${log4cplus_SOURCE_DIR}/include>
    $<BUILD_INTERFACE:${log4cplus_BINARY_DIR}/include>)
endif()

if (LOG4CPLUS_BUILD_LOGGINGSERVER)
  add_subdirectory (simpleserver)
endif (LOG4CPLUS_BUILD_LOGGINGSERVER)

if (LOG4CPLUS_BUILD_TESTING)
  add_subdirectory (tests)
endif (LOG4CPLUS_BUILD_TESTING)

#include(Log4CPlusCPack.cmake)

message (STATUS "Compiler: ${CMAKE_CXX_COMPILER}")
message (STATUS "${CMAKE_CXX_COMPILER_ID} version ${CMAKE_CXX_COMPILER_VERSION}")
message (STATUS "Compiler flags: ${CMAKE_CXX_FLAGS}")
message (STATUS "System name: ${CMAKE_SYSTEM_NAME}")
message (STATUS "System version: ${CMAKE_SYSTEM_VERSION}")

3. log4cplus库使用

基本构成:
Layout: 输出布局器,指定log信息的输出格式
Filter: 过滤器,过滤输出消息
Appender: 挂接器,指定log信息输出的介质
Logger: 记录器,保存并跟踪日志信息变更的实体
Category: 分类器,层次化的结,用于对被记录信息的分类,层中每个节点维护一个logger的所有信息;
Priority: 日志优先级,分为TRACE,DEBUG,INFO,WARNING,ERROR,FATAL

log4cplus的基本使用步骤如下所示:

  1. 实例化一个封装了输出介质的Appender对象,Appender对象决定了日志最终需要输出的位置,控制台,文件,远程服务器等;可在Appender中添加Filter过滤器,对日志信息进行过滤;
  2. 实例化一个封装了控制输出格式的layout对象,用户可通过layout对象指定日志的输出格式;layout主要有:SimpleLayout, PatternLayout, TTCCLayout三种:
    • SimpleLayout: 最简单的布局管理器,在原始的日志信息前面加上 -LogLevel
    • PatternLayout: 类似于printf函数的用法,能够对预定义的转换标识符进行解析,转换成特定格式进行输出;
    • TTCCLayout: 是一种在PatternLayout基础上发展而来的带缺省格式的输出布局器,其格式由时间,线程ID, logger, NDC (Nested Diagnostic Context)构成。主要是为了方便想显示典型信息又不想配置PatternLayout的用户。TTCCLayout可通过参数选择显示本地时间还是gmt时间;
  3. 将layout对象绑定(attach)到输出介质Appender对象上
  4. 实例化一个logger对象,logger对象可理解为一个日志记录器;可通过调用log4cplus::Logger::getInstance来实例化logger对象;
  5. 将Appender对象attach到日志记录器logger上;
  6. 设置logger的日志优先级;DEBUG < INFO < WARN < ERROR < FATAL

示例代码:

#include <stdio.h>
#include "config.h"
#include <string>
#include <log4cplus/log4cplus.h> 
#include <log4cplus/loglevel.h>
#include <memory>
#include <boost/format.hpp>

using namespace std;

int main(int argc, char** argv)
{
    
    // log4cplus::Initializer完成初始化
    log4cplus::Initializer initializer;

    // 建立Appander  立即刷新
    log4cplus::SharedAppenderPtr pAppender(new log4cplus::ConsoleAppender(false, true));
    pAppender->setName("ConsoleAppender");

    // L## 预处理阶段 L表示字符串使用unicode编码,##表示将两个字符串连接到一起
    log4cplus::tstring pattern = LOG4CPLUS_TEXT("%D{%m/%d/%y %H:%M:%S,%Q} [%t] %-5p %c - %m [%l]%n");
    std::unique_ptr<log4cplus::Layout> pLayout(new log4cplus::PatternLayout(pattern));
    pAppender->setLayout(std::move(pLayout));   // unique_ptr这里应该使用move

    // 创建logger
    log4cplus::Logger logger = log4cplus::Logger::getInstance("main");
    logger.setLogLevel(log4cplus::INFO_LOG_LEVEL);

    // 添加Appender到logger
    logger.addAppender(pAppender);

    for (size_t i = 0; i < 10; i++)
    {
        // boost中格式化字符串
        boost::format fmt = boost::format("Hellow ghost %1%.") % i;
        LOG4CPLUS_INFO(logger, LOG4CPLUS_TEXT("Hellow ghost #." << i)); 
        std::string info = fmt.str();    
        LOG4CPLUS_INFO(logger, LOG4CPLUS_TEXT(info.c_str()));
    }

    return 0;
}

日志输出格式:
img

log4cplus中appender的分类:

  • ConsoleAppender,将日志输出到控制台;
  • FileAppender: 将日志输出到指定文件中;
  • RollingFileAppender: 将日志输出到文件,实现滚动转储。初始化时用户需要设定文件大小,当日志文件超过设定的大小后(参数maxFileSize),会将后续的日志存储到新的文件中;除此之外,还需要设置最多允许新建的日志文件数量(参数maxBackupIndex),当日志的文件数量达到最大的数量后,则删除最早新建的日志文件,保证文件数量等于maxBackupIndex+1;
  • DailyRollingFileAppender:根绝设定的频度来决定是否转储日志文件;用户可将频度设定为MONTHLY, WEEKLY, DAILY, TWICE_DAYILY, HOURLY, MINUTELY;

示例代码:

/*
 * @FilePath: \helloworld\main\main.cpp
 * @Description: 
 * @Author: xxx
 * @Date: 2023-11-03 09:00:52
 * @LastEditTime: 2023-11-24 16:46:59
 */
#include <stdio.h>
#include "config.h"
#include <string>
#include "getopt.h"
#include <log4cplus/log4cplus.h> 
#include <log4cplus/loglevel.h>
#include <memory>
#include <boost/format.hpp>

using namespace std;

int main(int argc, char** argv)
{
    
    // log4cplus::Initializer完成初始化
    log4cplus::Initializer initializer;

    // 建立Appander  立即刷新
    log4cplus::SharedAppenderPtr pAppender(new log4cplus::ConsoleAppender(false, true));
    log4cplus::SharedAppenderPtr pFileAppender(new log4cplus::FileAppender("log1.txt", std::ios_base::app, true, false));
    log4cplus::SharedAppenderPtr pRollingFileAppender(new log4cplus::RollingFileAppender("rollog", 15, 3, true, false));
    pAppender->setName("ConsoleAppender");
    pFileAppender->setName("fileappend");
    pRollingFileAppender->setName("rollingappender");

    // 新建FileAppender immediateFlush:缓冲刷新标志,如果为true表示每向文件写一条记录就刷新一次缓存,
    // 否则直到FileAppender被关闭或文件缓存已满才更新文件,一般是要设置true的,比如你往文件写的过程中出现了错误
    //(如程序非正常退出),即使文件没有正常关闭也可以保证程序终止时刻之前的所有记录都会被正常保存

    // L## 预处理阶段 L表示字符串使用unicode编码,##表示将两个字符串连接到一起
    log4cplus::tstring pattern = LOG4CPLUS_TEXT("%D{%m/%d/%y %H:%M:%S,%Q} [%t] %-5p %c - %m [%l]%n");
    std::unique_ptr<log4cplus::Layout> pLayout(new log4cplus::PatternLayout(pattern));
    pAppender->setLayout(std::move(pLayout));   // unique_ptr这里应该使用move
    pFileAppender->setLayout(std::unique_ptr<log4cplus::Layout>(new log4cplus::PatternLayout(pattern)));
    pRollingFileAppender->setLayout(std::unique_ptr<log4cplus::Layout>(new log4cplus::PatternLayout(pattern)));

    // 创建logger
    log4cplus::Logger logger = log4cplus::Logger::getInstance("main");
    logger.setLogLevel(log4cplus::INFO_LOG_LEVEL);

    // 添加Appender到logger
    logger.addAppender(pAppender);
    logger.addAppender(pFileAppender);
    logger.addAppender(pRollingFileAppender);

    for (size_t i = 0; i < 100000000; i++)
    {
        // boost中格式化字符串
        boost::format fmt = boost::format("Hellow ghost %1%.") % i;
        // LOG4CPLUS_INFO(logger, LOG4CPLUS_TEXT("Hellow ghost #" << i)); //也可以 
        std::string info = fmt.str();    
        LOG4CPLUS_INFO(logger, LOG4CPLUS_TEXT(info.c_str()));
    }

    return 0;
}

posted @ 2023-11-18 10:07  Alpha205  阅读(921)  评论(0编辑  收藏  举报