Linux log工具:编译安装log4cplus及使用示例

1、(交叉)编译安装log4cplus

下载地址:

  https://github.com/log4cplus/log4cplus
  https://sourceforge.net/projects/log4cplus/

编译安装步骤:

tar xjvf log4cplus-2.0.6.tar.bz2
cd log4cplus-2.0.6
./configure 	# 交叉编译:./configure --prefix=$PWD/tmp --host=arm-linux-gnueabihf
make
make install

编译自己的程序:(编译时需要加上-std=c++11或-std=gnu++11选项对C++11的支持)

g++ mytest.cpp -o mytest -llog4cplus -std=gnu++11

2、log4cplus各种使用情景

2.1 参考docs/examples.md示例

#include <iomanip>
#include "log4cplus/logger.h"
#include "log4cplus/loggingmacros.h"
#include "log4cplus/configurator.h"
#include "log4cplus/initializer.h"

int main(int argc, char **argv)
{
    /* 类的实例化 */
    log4cplus::Initializer initializer;

    /* 使用布局配置root logger */
    log4cplus::BasicConfigurator config;
    config.configure();

    /* 获得一个logger句柄,名字叫“main” */
    log4cplus::Logger logger = log4cplus::Logger::getInstance(
							   LOG4CPLUS_TEXT("main"));

#if 1
    /* 设置打印等级,如果不手动设置则默认等级为DEBUG */
    logger.setLogLevel(log4cplus::INFO_LOG_LEVEL);
#endif
    
    /* 输出不同等级的log */
    LOG4CPLUS_TRACE(logger, LOG4CPLUS_TEXT("Hello, log4cplus!"));
    LOG4CPLUS_DEBUG(logger, LOG4CPLUS_TEXT("Hello, log4cplus!"));
    LOG4CPLUS_INFO (logger, LOG4CPLUS_TEXT("Hello, log4cplus!"));
    LOG4CPLUS_WARN (logger, LOG4CPLUS_TEXT("Hello, log4cplus!"));
    LOG4CPLUS_ERROR(logger, LOG4CPLUS_TEXT("Hello, log4cplus!"));
    LOG4CPLUS_FATAL(logger, LOG4CPLUS_TEXT("Hello, log4cplus!"));
    
    /* 支持“<<”符输出流 */
    LOG4CPLUS_INFO(logger, LOG4CPLUS_TEXT("This is a bool: ") << true);
    LOG4CPLUS_INFO(logger, LOG4CPLUS_TEXT("This is a char: ")
                  		<< LOG4CPLUS_TEXT('c'));
    LOG4CPLUS_INFO(logger, LOG4CPLUS_TEXT("This is a int: ") << 10);
    LOG4CPLUS_INFO(logger, LOG4CPLUS_TEXT("This is a unsigned long: ")
                  		<< static_cast<unsigned long>(100U));
    LOG4CPLUS_INFO(logger, LOG4CPLUS_TEXT("This is a hex: ")
                  		<< std::hex << 1000L);
    LOG4CPLUS_INFO(logger, LOG4CPLUS_TEXT("This is a float: ") << 1.2345f);
    LOG4CPLUS_INFO(logger, LOG4CPLUS_TEXT("This is a double: ")
                   		<< std::setprecision(15) << 1.23456789);
    LOG4CPLUS_INFO(logger, LOG4CPLUS_TEXT("This is a string: ")
                  		<< LOG4CPLUS_TEXT("a long string!"));
    
    return 0;
}

输出日志:

INFO - Hello, log4cplus!
WARN - Hello, log4cplus!
ERROR - Hello, log4cplus!
FATAL - Hello, log4cplus!
INFO - This is a bool: 1
INFO - This is a char: c
INFO - This is a int: 10
INFO - This is a unsigned long: 100
INFO - This is a hex: 3e8
INFO - This is a float: 1.2345
INFO - This is a double: 1.23456789
INFO - This is a string: a long string!

其中,优先级在include/log4cplus/loglevel.h中定义:

const LogLevel OFF_LOG_LEVEL     = 60000;
const LogLevel FATAL_LOG_LEVEL   = 50000;
const LogLevel ERROR_LOG_LEVEL   = 40000;
const LogLevel WARN_LOG_LEVEL    = 30000;
const LogLevel INFO_LOG_LEVEL    = 20000;
const LogLevel DEBUG_LOG_LEVEL   = 10000;
const LogLevel TRACE_LOG_LEVEL   = 0;
const LogLevel ALL_LOG_LEVEL     = TRACE_LOG_LEVEL;
const LogLevel NOT_SET_LOG_LEVEL = -1;

2.2 比较标准的使用方法(附SimpleLayout/PatternLayout/TTCCLayout)

#include <iomanip>
#include "log4cplus/logger.h"
#include "log4cplus/consoleappender.h"
#include "log4cplus/loglevel.h"
#include "log4cplus/loggingmacros.h"
#include "log4cplus/initializer.h"

using namespace std;
using namespace log4cplus;

/* 支持三种layout方式 */
#define SIMPLE_LAYOUT 	0
#define PATTERN_LAYOUT 	1
#define TTCC_LAYOUT 	0

int main(int argc, char **argv)
{
    /* 1、先进行log4cplus的实例化 */
    log4cplus::Initializer initializer;
    
    /* 2、实例化一个输出介质的appender对象,这里使用的是控制台console */
    SharedAppenderPtr append_1(new ConsoleAppender());
    append_1->setName(LOG4CPLUS_TEXT("append_1"));
    
    /* 3、实例化一个输出格式的layout对象,并将layout与appender绑定(可选) */
#if(SIMPLE_LAYOUT == 1)
    append_1->setLayout(std::unique_ptr<Layout>(new SimpleLayout()));
#elif(PATTERN_LAYOUT == 1)
    log4cplus::tstring pattern =  LOG4CPLUS_TEXT("%D{%Y/%m/%d %H:%M:%S,%Q} [%t] %-5p - %m [%l]%n");
	append_1->setLayout(std::unique_ptr<Layout>(new PatternLayout(pattern)));
#elif(TTCC_LAYOUT == 1)
    append_1->setLayout(std::unique_ptr<Layout>(new TTCCLayout()));
#endif

    /* 4、实例化一个日志输出的logger对象,并调用getInstance获得示例 */
    Logger logger_1 = Logger::getInstance(LOG4CPLUS_TEXT("test1"));
    
    /* 5、将appender与logger绑定 */
    logger_1.addAppender(append_1);
    
    /* 6、设置打印等级(可选) */
    logger_1.setLogLevel(log4cplus::WARN_LOG_LEVEL);
                      
    /* 7、输出不同等级的log */
    LOG4CPLUS_TRACE(logger_1, LOG4CPLUS_TEXT("Hello, log4cplus!"));
    LOG4CPLUS_DEBUG(logger_1, LOG4CPLUS_TEXT("Hello, log4cplus!"));
    LOG4CPLUS_INFO (logger_1, LOG4CPLUS_TEXT("Hello, log4cplus!"));
    LOG4CPLUS_WARN (logger_1, LOG4CPLUS_TEXT("Hello, log4cplus!"));
    LOG4CPLUS_ERROR(logger_1, LOG4CPLUS_TEXT("Hello, log4cplus!"));
    LOG4CPLUS_FATAL(logger_1, LOG4CPLUS_TEXT("Hello, log4cplus!"));
    
	return 0;   
}

输出日志:

2021/04/09 11:29:28,370.635 [139879316391744] WARN  - Hello, log4cplus! [test_2.cpp:48]
2021/04/09 11:29:28,370.744 [139879316391744] ERROR - Hello, log4cplus! [test_2.cpp:49]
2021/04/09 11:29:28,370.790 [139879316391744] FATAL - Hello, log4cplus! [test_2.cpp:50]

对于PatterLayout格式控制,主要包括以下标识符:

标识符说明
%%转义为%
%clogger名称
%D%A 礼拜全称;
%a 礼拜缩写;
%B 月份全称;
%b 月份缩写
%c 标准时间+时间格式;
%d 日期;
%H 小时;
%I 几时(1-12);
%j 哪一天(1-366)
%m 月份
%M 分钟
%p AM/PM
%q 毫秒
%Q 带小数的毫秒
%S 秒
%U 第几个礼拜,以周日为第一天
%W 第几个礼拜,以周一为第一天
%w 礼拜几
%x 标准日期格式
%X 标准时间格式
%y 两位数年份
%Y 四位数年份
%Z 时区名
%d标准时间
%F文件名称
%L文件行号
%l文件名称及行号
%m日志内容
%n换行符
%p日志等级
%t线程ID
%x嵌套诊断上下文
-数值左对齐

2.3 比较简洁的使用方法

#include <iomanip>
#include "log4cplus/logger.h"
#include "log4cplus/consoleappender.h"
#include "log4cplus/loglevel.h"
#include "log4cplus/loggingmacros.h"
#include "log4cplus/initializer.h"

using namespace std;
using namespace log4cplus;

int main(int argc, char **argv)
{
    /* 1、先进行log4cplus的实例化 */
    log4cplus::Initializer initializer;
    
    /* 2、实例化一个输出介质的appender对象,这里使用的是控制台console */
    SharedAppenderPtr append_1(new ConsoleAppender());
    append_1->setName(LOG4CPLUS_TEXT("append_1"));

    /* 4、实例化一个日志输出的logger对象,并调用getInstance获得示例 */
    Logger logger_1 = Logger::getInstance(LOG4CPLUS_TEXT("test1"));
    
    /* 5、将appender与logger绑定 */
    logger_1.addAppender(append_1);

    /* 7、输出不同等级的log */
    LOG4CPLUS_TRACE(logger_1, LOG4CPLUS_TEXT("Hello, log4cplus!"));
    LOG4CPLUS_DEBUG(logger_1, LOG4CPLUS_TEXT("Hello, log4cplus!"));
    LOG4CPLUS_INFO (logger_1, LOG4CPLUS_TEXT("Hello, log4cplus!"));
    LOG4CPLUS_WARN (logger_1, LOG4CPLUS_TEXT("Hello, log4cplus!"));
    LOG4CPLUS_ERROR(logger_1, LOG4CPLUS_TEXT("Hello, log4cplus!"));
    LOG4CPLUS_FATAL(logger_1, LOG4CPLUS_TEXT("Hello, log4cplus!"));
    
	return 0;   
}

2.4 使用loglog输出日志

LogLog是用于log4cplus运行时显示自身的调试等信息,是对标准输出的封装。其中提供了setInternalDebugging()方法用来控制是否屏蔽输出信息的调试信息,默认为false屏蔽;setQuietMode()则是用来设置是否屏蔽所有输出信息,默认为false不屏蔽。

/* 出自源码目录tests/loglog_test/main.cxx */
#include <iostream>
#include "log4cplus/helpers/loglog.h"
#include "log4cplus/logger.h"
#include "log4cplus/initializer.h"

using namespace std;
using namespace log4cplus::helpers;

void printMsgs() {
    cout << "Entering printMsgs()..." << endl;

    LogLog::getLogLog()->debug(LOG4CPLUS_TEXT("This is a Debug statement..."));
    LogLog::getLogLog()->debug(
        log4cplus::tstring(LOG4CPLUS_TEXT("This is a Debug statement...")));

    LogLog::getLogLog()->warn(LOG4CPLUS_TEXT("This is a Warning..."));
    LogLog::getLogLog()->warn(
        log4cplus::tstring(LOG4CPLUS_TEXT("This is a Warning...")));

    LogLog::getLogLog()->error(LOG4CPLUS_TEXT("This is a Error..."));
    LogLog::getLogLog()->error(
        log4cplus::tstring(LOG4CPLUS_TEXT("This is a Error...")));

    cout << "Exiting printMsgs()..." << endl << endl;
}


int main(int argc, char** argv)
{
    log4cplus::Initializer initializer;
    printMsgs();

    cout << "Turning on debug..." << endl;
    LogLog::getLogLog()->setInternalDebugging(true);
    printMsgs();

    cout << "Turning on quiet mode..." << endl;
    LogLog::getLogLog()->setQuietMode(true);
    printMsgs();

    return 0;
}

输出日志:

Entering printMsgs()...
log4cplus:WARN This is a Warning...
log4cplus:WARN This is a Warning...
log4cplus:ERROR This is a Error...
log4cplus:ERROR This is a Error...
Exiting printMsgs()...

Turning on debug...
Entering printMsgs()...
log4cplus: This is a Debug statement...
log4cplus: This is a Debug statement...
log4cplus:WARN This is a Warning...
log4cplus:WARN This is a Warning...
log4cplus:ERROR This is a Error...
log4cplus:ERROR This is a Error...
Exiting printMsgs()...

Turning on quiet mode...
Entering printMsgs()...
Exiting printMsgs()...

2.5 使用层次化结构来组织logger

在log4cplus中,所有的logger都是通过层次化的结构来组织。首先是root级别的logger,它是通过以下方法获取:

Logger root = Logger::getRoot(); // 注意:它和Logger::getInstance("root")是不等价的

其次是用户自定义的logger,获取示例时就赋予了名称:

Logger test = Logger::getInstance("test");

此外,用户还可以定义logger的子logger:

Logger subTest = Logger::getInstance("test.subtest");

各个层级可以调用自身的setLogLevel函数来设置自己的优先级:

root.setLogLevel(...);
Test.setLogLevel(...);
subTest.setLogLevel(...);

注:当某个logger的LogLevel设置为“NOT_SET_LOG_LEVEL”时,该logger会继承父logger的优先级。如果定义的logger出现重名情况,则修改其中一个都会改变其他logger。

示例程序:

/* 出自源码目录tests/customloglevel_test/main.cxx */
#include <iomanip>
#include "log4cplus/logger.h"
#include "log4cplus/consoleappender.h"
#include "log4cplus/loglevel.h"
#include "log4cplus/loggingmacros.h"
#include "log4cplus/initializer.h"

using namespace std;
using namespace log4cplus;


int main(int argc, char **argv)
{
    /* 先进行log4cplus的实例化 */
    log4cplus::Initializer initializer;
    
    /* 实例化一个输出介质的appender对象,这里使用的是控制台console */
    SharedAppenderPtr append_1(new ConsoleAppender());
    append_1->setName(LOG4CPLUS_TEXT("append_1"));
    
    /* 将logger与appender进行绑定 */
    Logger::getRoot().addAppender(append_1);
    
    /* 层级结构 */
    Logger root = Logger::getRoot();
    Logger test = Logger::getInstance(LOG4CPLUS_TEXT("test"));
    Logger subTest = Logger::getInstance(LOG4CPLUS_TEXT("test.subtest"));
    
    /* 用于查看层级优先级 */
    LogLevelManager& llm = getLogLevelManager();
    
    /* 查看层级之间设置优先级的关系 */
    LOG4CPLUS_FATAL(root, "Before setting LogLevel");
	LOG4CPLUS_FATAL(root, "root: " << llm.toString(root.getChainedLogLevel()));
	LOG4CPLUS_FATAL(root, "test: " << llm.toString(test.getChainedLogLevel()));
    LOG4CPLUS_FATAL(root, "test.subtest: " << llm.toString(subTest.getChainedLogLevel()));
    
    LOG4CPLUS_FATAL(root, "Setting test.subtest to WARN");
    subTest.setLogLevel(log4cplus::WARN_LOG_LEVEL);
	LOG4CPLUS_FATAL(root, "root: " << llm.toString(root.getChainedLogLevel()));
	LOG4CPLUS_FATAL(root, "test: " << llm.toString(test.getChainedLogLevel()));
	LOG4CPLUS_FATAL(root, "test.subtest: " << llm.toString(subTest.getChainedLogLevel()));
    
	LOG4CPLUS_FATAL(root, "Setting test to ERROR");
	test.setLogLevel(log4cplus::ERROR_LOG_LEVEL);
	LOG4CPLUS_FATAL(root, "root: " << llm.toString(root.getChainedLogLevel()));
	LOG4CPLUS_FATAL(root, "test: " << llm.toString(test.getChainedLogLevel()));
	LOG4CPLUS_FATAL(root, "test.subtest: " << llm.toString(subTest.getChainedLogLevel()));
    
	LOG4CPLUS_FATAL(root, "Setting test.subtest to NOT_SET_LOG_LEVEL");
	subTest.setLogLevel(log4cplus::NOT_SET_LOG_LEVEL);
	LOG4CPLUS_FATAL(root, "root: " << llm.toString(root.getChainedLogLevel()));
	LOG4CPLUS_FATAL(root, "test: " << llm.toString(test.getChainedLogLevel()));
	LOG4CPLUS_FATAL(root, "test.subtest: " << llm.toString(subTest.getChainedLogLevel()));
    
    /* 各个层级之间输出log */
    LOG4CPLUS_FATAL(root, "Now, test the loggers at all levels");
    LOG4CPLUS_INFO(root, LOG4CPLUS_TEXT("Hello, log4cplus! - root"));
    LOG4CPLUS_WARN(test, LOG4CPLUS_TEXT("Hello, log4cplus! - test"));
    LOG4CPLUS_ERROR(subTest, LOG4CPLUS_TEXT("Hello, log4cplus! - subTest"));
    
	return 0;   
}

输出日志:

FATAL - Before setting LogLevel
FATAL - root: DEBUG
FATAL - test: DEBUG
FATAL - test.subtest: DEBUG
FATAL - Setting test.subtest to WARN
FATAL - root: DEBUG
FATAL - test: DEBUG
FATAL - test.subtest: WARN
FATAL - Setting test to ERROR
FATAL - root: DEBUG
FATAL - test: ERROR
FATAL - test.subtest: WARN
FATAL - Setting test.subtest to NOT_SET_LOG_LEVEL
FATAL - root: DEBUG
FATAL - test: ERROR
FATAL - test.subtest: ERROR
FATAL - Now, test the loggers at all levels
INFO - Hello, log4cplus! - root
ERROR - Hello, log4cplus! - subTest

2.6 重定向到文件

三种FileAppender构造函数:

/* 1、一般文件输出 */
FileAppender(const log4cplus::tstring& filename, /* 文件名 */
             std::ios_base::openmode mode = std::ios_base::trunc, /* 文件类型,默认trunc将先前文件删除 */
             bool immediateFlush = true, /* 是否每次写一条记录都刷新一次缓存,一般为true */
             bool createDirs = false); 	 /* 是否创建目录 */

/* 2、按日志文件大小输出 */
RollingFileAppender(const log4cplus::tstring& filename,
                    long maxFileSize = 10*1024*1024, /* 文件最大尺寸,默认10MB */
                    int maxBackupIndex = 1, 		 /* 最大记录文件数 */
                    bool immediateFlush = true,
                    bool createDirs = false);

/* 3、按时间周期输出 */
DailyRollingFileAppender(const log4cplus::tstring& filename,
                         DailyRollingFileSchedule schedule = DAILY, 
                         /* 存储频度:MONTHLY/WEEKLY/DAILY/TWICE_DAILY/HOURLY/MINUTELY */
                         bool immediateFlush = true,
                         int maxBackupIndex = 10,
                         bool createDirs = false,
                         bool rollOnClose = true,
                         const log4cplus::tstring& datePattern = log4cplus::tstring());

示例程序:

#include <iomanip>
#include <unistd.h>
#include "log4cplus/logger.h"
#include "log4cplus/fileappender.h"
#include "log4cplus/loglevel.h"
#include "log4cplus/loggingmacros.h"
#include "log4cplus/initializer.h"

using namespace std;
using namespace log4cplus;

#define FILE_APPENDER 				0
#define ROLLINGFILE_APPENDER 		1
#define DAILYROLLINGFILE_APPENDER 	0

int main(int argc, char **argv)
{
    log4cplus::Initializer initializer;
    
#if (FILE_APPENDER == 1)
    SharedAppenderPtr append_1(new FileAppender("test.log"));
#elif (ROLLINGFILE_APPENDER == 1)
    SharedAppenderPtr append_1(new RollingFileAppender("./log/test.log", 200*1024, 5, true, true));
#elif (DAILYROLLINGFILE_APPENDER == 1)
    SharedAppenderPtr append_1(new DailyRollingFileAppender("test.log", MINUTELY, true, 5));
#endif
    append_1->setName(LOG4CPLUS_TEXT("append_1"));

    log4cplus::tstring pattern =  LOG4CPLUS_TEXT("%D{%Y/%m/%d %H:%M:%S,%Q} [%t] %-5p - %m [%l]%n");
	append_1->setLayout(std::unique_ptr<Layout>(new PatternLayout(pattern)));
    
    Logger logger_1 = Logger::getInstance(LOG4CPLUS_TEXT("test1"));
    
    logger_1.addAppender(append_1);

    logger_1.setLogLevel(log4cplus::WARN_LOG_LEVEL);
    
    while(true){
        LOG4CPLUS_TRACE(logger_1, LOG4CPLUS_TEXT("Hello, log4cplus!"));
        LOG4CPLUS_DEBUG(logger_1, LOG4CPLUS_TEXT("Hello, log4cplus!"));
        LOG4CPLUS_INFO (logger_1, LOG4CPLUS_TEXT("Hello, log4cplus!"));
        LOG4CPLUS_WARN (logger_1, LOG4CPLUS_TEXT("Hello, log4cplus!"));
        LOG4CPLUS_ERROR(logger_1, LOG4CPLUS_TEXT("Hello, log4cplus!"));
        LOG4CPLUS_FATAL(logger_1, LOG4CPLUS_TEXT("Hello, log4cplus!"));
        usleep(100);
    }
    
	return 0;   
}

2.7 使用脚本配置文件进行设置

配置文件(自定义名字为log4cplus.properties):

# 1、rootLogger/non-rootlogger配置
# root语法:log4cplus.rootLogger=[LogLevel], appenderName, appenderName, ...
# non-root语法:log4cplus.logger.logger_name=[LogLevel|INHERITED], appenderName, appenderName, ...
log4cplus.rootLogger=TRACE, ALL_MSGS, TRACE_MSGS, DEBUG_INFO_MSGS, FATAL_MSGS

# 2、Appender配置
# log4cplus.appender.appenderName=xxx
# 	xxx可选:ConsoleAppender/FileAppender/RollingFileAppender/DailyRollingFileAppender/SocketAppender
#
# 2.1 File通用配置
# 	ImmediateFlush:是否立即刷新,默认true
# 	File:保存的文件名
# 	layout:输出格式
# 	Append:是否追加到之前的文件
# 	ReopenDelay:指定时间间隔往文件中写入,单位为s
# 	UseLockFile:是否使用加锁文件,默认不使用
# 	LockFile:加锁文件名
# 	Locale:使用的字符集
# 	Threshold:指定日志消息的输出最低层次
# 
# 2.2 DailyRollingFileAppender配置选项
# 	Schedule:保存频率,可选MONTHLY/WEEKLY/DAILY/TWICE_DAILY/HOURLY/MINUTELY
# 	MaxBackupIndex:最多文件保存个数
# 	DatePattern:日期格式,以'.'yyyy-MM-dd-HH-mm形式定义
# 
# 2.3 RollingFileAppender配置选项
# 	MaxFileSize:最大文件大小,最小为200KB,单位还有MB
# 	MaxBackupIndex:最多文件保存个数
# 
# 3、Layout配置
# log4cplus.appender.appenderName.layout=xxx
# 	xxx可选:SimpleLayout/PatternLayout/TTCCLayout;默认为SimpleLayout
# 	PatternLayout的设置示例:
# 		log4cplus.appender.appenderName.layout.ConversionPattern=%d{%m/%d/%y %H:%M:%S,%Q} [%t] %-5p - %m%n
log4cplus.appender.ALL_MSGS=log4cplus::RollingFileAppender
log4cplus.appender.ALL_MSGS.ImmediateFlush=true
log4cplus.appender.ALL_MSGS.File=all_msgs.log
log4cplus.appender.ALL_MSGS.layout=log4cplus::PatternLayout
log4cplus.appender.ALL_MSGS.layout.ConversionPattern=%D{%Y/%m/%d %H:%M:%S,%Q} [tread_id: %t] %-5p - %m%n
#log4cplus.appender.ALL_MSGS.Append=true
#log4cplus.appender.ALL_MSGS.ReopenDelay=10
#log4cplus.appender.ALL_MSGS.UseLockFile=true
#log4cplus.appender.ALL_MSGS.LockFile=fuck_are_you.lock
#log4cplus.appender.ALL_MSGS.Locale=chs
#log4cplus.appender.ALL_MSGS.Threshold=DEBUG

# 4、Filter配置
# Appender可以附加Filter组成的链表
# log4cplus.appender.appenderName.Filters.FilterNumber=xxx
# 	xxx选项说明:
# 	 DenyAllFilter:过滤掉所有
# 	 LogLevelMatchFilter:只有优先级匹配才会匹配,并且AcceptOnMatch为true才匹配
# 	 LogLevelRangeFilter:根据优先级范围进行过滤
# 	 StringMatchFilter:根据日志内容包含特定字符串进行过滤
log4cplus.appender.TRACE_MSGS=log4cplus::RollingFileAppender
log4cplus.appender.TRACE_MSGS.File=trace_msgs.log
log4cplus.appender.TRACE_MSGS.layout=log4cplus::TTCCLayout
log4cplus.appender.TRACE_MSGS.filters.1=log4cplus::spi::LogLevelMatchFilter
log4cplus.appender.TRACE_MSGS.filters.1.LogLevelToMatch=TRACE
log4cplus.appender.TRACE_MSGS.filters.1.AcceptOnMatch=true
log4cplus.appender.TRACE_MSGS.filters.2=log4cplus::spi::DenyAllFilter

log4cplus.appender.DEBUG_INFO_MSGS=log4cplus::RollingFileAppender
log4cplus.appender.DEBUG_INFO_MSGS.File=debug_info_msgs.log
log4cplus.appender.DEBUG_INFO_MSGS.layout=log4cplus::TTCCLayout
log4cplus.appender.DEBUG_INFO_MSGS.filters.1=log4cplus::spi::LogLevelRangeFilter
log4cplus.appender.DEBUG_INFO_MSGS.filters.1.LogLevelMin=DEBUG
log4cplus.appender.DEBUG_INFO_MSGS.filters.1.LogLevelMax=INFO
log4cplus.appender.DEBUG_INFO_MSGS.filters.1.AcceptOnMatch=true
log4cplus.appender.DEBUG_INFO_MSGS.filters.2=log4cplus::spi::DenyAllFilter

log4cplus.appender.FATAL_MSGS=log4cplus::RollingFileAppender
log4cplus.appender.FATAL_MSGS.File=fatal_msgs.log
log4cplus.appender.FATAL_MSGS.layout=log4cplus::TTCCLayout
log4cplus.appender.FATAL_MSGS.filters.1=log4cplus::spi::StringMatchFilter
log4cplus.appender.FATAL_MSGS.filters.1.StringToMatch=Hello
log4cplus.appender.FATAL_MSGS.filters.1.AcceptOnMatch=true
log4cplus.appender.FATAL_MSGS.filters.2=log4cplus::spi::DenyAllFilter

应用程序:

#include <iomanip>
#include "log4cplus/logger.h"
#include "log4cplus/consoleappender.h"
#include "log4cplus/loglevel.h"
#include "log4cplus/loggingmacros.h"
#include "log4cplus/initializer.h"
#include "log4cplus/configurator.h"

using namespace std;
using namespace log4cplus;

int main(int argc, char **argv)
{
    log4cplus::Initializer initializer;   
    
    Logger root = Logger::getRoot();
	Logger logger_1 = Logger::getInstance(LOG4CPLUS_TEXT("test1"));
    
	/* 使用配置文件进行设置 */
   	log4cplus::PropertyConfigurator::doConfigure(LOG4CPLUS_TEXT("./log4cplus.properties"));

    LOG4CPLUS_TRACE(logger_1, LOG4CPLUS_TEXT("Hello, log4cplus!"));
    LOG4CPLUS_DEBUG(logger_1, LOG4CPLUS_TEXT("Hello, log4cplus!"));
    LOG4CPLUS_INFO (logger_1, LOG4CPLUS_TEXT("Hello, log4cplus!"));
    LOG4CPLUS_WARN (logger_1, LOG4CPLUS_TEXT("Hello, log4cplus!"));
    LOG4CPLUS_ERROR(logger_1, LOG4CPLUS_TEXT("Hello, log4cplus!"));
    LOG4CPLUS_FATAL(logger_1, LOG4CPLUS_TEXT("Hello, log4cplus!"));

	return 0;   
}

查看输出的log日志内容:

# cat all_msgs.log 
2021/04/09 11:43:12,417.667 [tread_id: 140423394019136] TRACE - Hello, log4cplus!
2021/04/09 11:43:12,417.733 [tread_id: 140423394019136] DEBUG - Hello, log4cplus!
2021/04/09 11:43:12,417.749 [tread_id: 140423394019136] INFO  - Hello, log4cplus!
2021/04/09 11:43:12,417.763 [tread_id: 140423394019136] WARN  - Hello, log4cplus!
2021/04/09 11:43:12,417.772 [tread_id: 140423394019136] ERROR - Hello, log4cplus!
2021/04/09 11:43:12,417.782 [tread_id: 140423394019136] FATAL - Hello, log4cplus!
# cat trace_msgs.log 
0 [140423394019136] TRACE test1 <> - Hello, log4cplus!
# cat debug_info_msgs.log 
0 [140423394019136] DEBUG test1 <> - Hello, log4cplus!
0 [140423394019136] INFO test1 <> - Hello, log4cplus!
# cat fatal_msgs.log 
0 [140423394019136] TRACE test1 <> - Hello, log4cplus!
0 [140423394019136] DEBUG test1 <> - Hello, log4cplus!
0 [140423394019136] INFO test1 <> - Hello, log4cplus!
0 [140423394019136] WARN test1 <> - Hello, log4cplus!
0 [140423394019136] ERROR test1 <> - Hello, log4cplus!
0 [140423394019136] FATAL test1 <> - Hello, log4cplus!

2.8 动态加载配置文件

#include <iomanip>
#include <unistd.h>
#include "log4cplus/logger.h"
#include "log4cplus/consoleappender.h"
#include "log4cplus/loglevel.h"
#include "log4cplus/loggingmacros.h"
#include "log4cplus/initializer.h"
#include "log4cplus/configurator.h"

using namespace std;
using namespace log4cplus;

int main(int argc, char **argv)
{
    log4cplus::Initializer initializer;   
    
    Logger root = Logger::getRoot();
	Logger logger_1 = Logger::getInstance(LOG4CPLUS_TEXT("test1"));
    
	/* 使用一个线程去监控配置文件实现动态配置,时间间隔单位为ms,默认为60*1000 */
   	ConfigureAndWatchThread configureThread("log4cplus.properties", 5 * 1000);

    while(1){
		LOG4CPLUS_TRACE(logger_1, LOG4CPLUS_TEXT("Hello, log4cplus!"));
		LOG4CPLUS_DEBUG(logger_1, LOG4CPLUS_TEXT("Hello, log4cplus!"));
		LOG4CPLUS_INFO (logger_1, LOG4CPLUS_TEXT("Hello, log4cplus!"));
		LOG4CPLUS_WARN (logger_1, LOG4CPLUS_TEXT("Hello, log4cplus!"));
		LOG4CPLUS_ERROR(logger_1, LOG4CPLUS_TEXT("Hello, log4cplus!"));
		LOG4CPLUS_FATAL(logger_1, LOG4CPLUS_TEXT("Hello, log4cplus!"));
        sleep(2);
    }

	return 0;
}

2.9 重定向到远程服务器

log4cplus提供了SocketAppender,采用的是client/server的通讯模式。客户端只管将log重定向到远程服务器,至于格式的配置方面由服务器端来决定。在本例中的测试程序均来源于log4cplus的测试示例,服务器端的配置文件则可以继续使用2.7中的“log4cplus.properties”。

服务器端(出自源码目录simpleserver/loggingserver.cxx):

#include <cstdlib>
#include <list>
#include <iostream>
#include "log4cplus/configurator.h"
#include "log4cplus/socketappender.h"
#include "log4cplus/helpers/socket.h"
#include "log4cplus/thread/threads.h"
#include "log4cplus/spi/loggingevent.h"
#include "log4cplus/thread/syncprims.h"
#include "log4cplus/log4cplus.h"


namespace loggingserver
{

typedef std::list<log4cplus::thread::AbstractThreadPtr> ThreadQueueType;

class ReaperThread : public log4cplus::thread::AbstractThread
{
public:
    ReaperThread (log4cplus::thread::Mutex & mtx_,
        log4cplus::thread::ManualResetEvent & ev_,
        ThreadQueueType & queue_)
        : mtx (mtx_)
        , ev (ev_)
        , queue (queue_)
        , stop (false)
    { }

    virtual
    ~ReaperThread ()
    { }

    virtual void run ();

    void signal_exit ();

private:
    log4cplus::thread::Mutex & mtx;
    log4cplus::thread::ManualResetEvent & ev;
    ThreadQueueType & queue;
    bool stop;
};

typedef log4cplus::helpers::SharedObjectPtr<ReaperThread> ReaperThreadPtr;

void ReaperThread::signal_exit ()
{
    log4cplus::thread::MutexGuard guard (mtx);
    stop = true;
    ev.signal ();
}

void ReaperThread::run ()
{
    ThreadQueueType q;

    while (true)
    {
        ev.timed_wait (30 * 1000);

        {
            log4cplus::thread::MutexGuard guard (mtx);

            // Check exit condition as the very first thing.
            if (stop)
            {
                std::cout << "Reaper thread is stopping..." << std::endl;
                return;
            }

            ev.reset ();
            q.swap (queue);
        }

        if (! q.empty ())
        {
            std::cout << "Reaper thread is reaping " << q.size () << " threads."
                      << std::endl;

            for (ThreadQueueType::iterator it = q.begin (), end_it = q.end ();
                 it != end_it; ++it)
            {
                AbstractThread & t = **it;
                t.join ();
            }

            q.clear ();
        }
    }
}

/**
   This class wraps ReaperThread thread and its queue.
 */
class Reaper
{
public:
    Reaper ()
    {
        reaper_thread = ReaperThreadPtr (new ReaperThread (mtx, ev, queue));
        reaper_thread->start ();
    }

    ~Reaper ()
    {
        reaper_thread->signal_exit ();
        reaper_thread->join ();
    }

    void visit (log4cplus::thread::AbstractThreadPtr const & thread_ptr);

private:
    log4cplus::thread::Mutex mtx;
    log4cplus::thread::ManualResetEvent ev;
    ThreadQueueType queue;
    ReaperThreadPtr reaper_thread;
};

void Reaper::visit (log4cplus::thread::AbstractThreadPtr const & thread_ptr)
{
    log4cplus::thread::MutexGuard guard (mtx);
    queue.push_back (thread_ptr);
    ev.signal ();
}

class ClientThread
    : public log4cplus::thread::AbstractThread
{
public:
    ClientThread(log4cplus::helpers::Socket clientsock_, Reaper & reaper_)
        : self_reference (log4cplus::thread::AbstractThreadPtr (this))
        , clientsock(std::move (clientsock_))
        , reaper (reaper_)
    {
        std::cout << "Received a client connection!!!!" << std::endl;
    }

    ~ClientThread()
    {
        std::cout << "Client connection closed." << std::endl;
    }

    virtual void run();

private:
    log4cplus::thread::AbstractThreadPtr self_reference;
    log4cplus::helpers::Socket clientsock;
    Reaper & reaper;
};


void loggingserver::ClientThread::run()
{
    try{
        while (1){
            if (!clientsock.isOpen())
                break;

            log4cplus::helpers::SocketBuffer msgSizeBuffer(sizeof(unsigned int));
            if (!clientsock.read(msgSizeBuffer))
                break;

            unsigned int msgSize = msgSizeBuffer.readInt();

            log4cplus::helpers::SocketBuffer buffer(msgSize);
            if (!clientsock.read(buffer))
                break;

            log4cplus::spi::InternalLoggingEvent event
                = log4cplus::helpers::readFromBuffer(buffer);
            log4cplus::Logger logger
                = log4cplus::Logger::getInstance(event.getLoggerName());
            logger.callAppenders(event);
        }
    }
    catch (...) {
        reaper.visit (std::move (self_reference));
        throw;
    }

    reaper.visit (std::move (self_reference));
}

} // namespace loggingserver

int main(int argc, char** argv)
{
    log4cplus::Initializer initializer;

    if(argc < 4){
        std::cout << "Usage: host port config_file [<IP version>]\n"
            << "<IP version> either 0 for IPv4 (default) or 1 for IPv6\n"
            << std::flush;
        return 1;
    }
    int const port = std::atoi(argv[2]);
    bool const ipv6 = argc >= 5 ? !!std::atoi(argv[4]) : false;
    const log4cplus::tstring configFile = LOG4CPLUS_C_STR_TO_TSTRING(argv[3]);

    log4cplus::PropertyConfigurator config(configFile);
    config.configure();

    log4cplus::helpers::ServerSocket serverSocket(port, false, ipv6,
        LOG4CPLUS_C_STR_TO_TSTRING(argv[1]));
    if (!serverSocket.isOpen()){
        std::cerr << "Could not open server socket, maybe port "
            << port << " is already in use." << std::endl;
        return 2;
    }

    loggingserver::Reaper reaper;

    for (;;){
        loggingserver::ClientThread *thr =
            new loggingserver::ClientThread(serverSocket.accept(), reaper);
        thr->start();
    }

    return 0;
}

客户端(出自源码目录tests/socket_test/main.cxx):

#include "log4cplus/log4cplus.h"
#include <iomanip>

using namespace std;
using namespace log4cplus;

int
main(int argc, char **argv)
{
    log4cplus::Initializer initializer;

    std::this_thread::sleep_for (std::chrono::seconds (1));
    tstring serverName = (argc > 1
        ? LOG4CPLUS_C_STR_TO_TSTRING(argv[1]) : tstring());
    tstring host = LOG4CPLUS_TEXT("localhost");
    SharedAppenderPtr append_1(new SocketAppender(host, 9998, serverName));
    append_1->setName( LOG4CPLUS_TEXT("First") );
    Logger::getRoot().addAppender(append_1);

    Logger root = Logger::getRoot();
    Logger test = Logger::getInstance( LOG4CPLUS_TEXT("socket.test") );

    LOG4CPLUS_DEBUG(root,
                    "This is"
                    << " a reall"
                    << "y long message." << endl
                    << "Just testing it out" << endl
                    << "What do you think?");
    test.setLogLevel(NOT_SET_LOG_LEVEL);
    LOG4CPLUS_DEBUG(test, "This is a bool: " << true);
    LOG4CPLUS_INFO(test, "This is a char: " << 'x');
    LOG4CPLUS_INFO(test, "This is a short: " << static_cast<short>(-100));
    LOG4CPLUS_INFO(test, "This is a unsigned short: "
        << static_cast<unsigned short>(100));
    std::this_thread::sleep_for (std::chrono::microseconds (500));
    LOG4CPLUS_INFO(test, "This is a int: " << 1000);
    LOG4CPLUS_INFO(test, "This is a unsigned int: " << 1000u);
    LOG4CPLUS_INFO(test, "This is a long(hex): " << hex << 100000000l);
    LOG4CPLUS_INFO(test, "This is a unsigned long: " << 100000000ul);
    LOG4CPLUS_WARN(test, "This is a float: " << 1.2345f);
    LOG4CPLUS_ERROR(test,
                    "This is a double: "
                    << setprecision(15)
                    << 1.2345234234);
    LOG4CPLUS_FATAL(test,
                    "This is a long double: "
                    << setprecision(15)
                    << 123452342342.342L);

    return 0;
}

2.10 嵌入诊断上下文NDC

NDC(Nested Diagnostic Context),对于多个输入源的log系统(例如前面重定向到服务器程序),NDC可以为信息打上一个标签,使得容易辨认区分。这个标签是线程特有的,利用的是线程局部存储机制,称为线程私有数据(TSD,Thread-Specific Data)。NDC的使用比较简单:

① 在某个线程中使用

NDC& ndc = log4cplus::getNDC();
ndc.push("ur ndc string");
LOG4CPLUS_DEBUG(logger, "This is a NDC log!");
...
ndc.pop();
LOG4CPLUS_DEBUG(logger, "This is not a NDC log!");
ndc.remove();

输出日志(使用TTCCLayout情况下):

0 [140622797649728] DEBUG test1 <ur ndc string> - This is a NDC log!
0 [140622797649728] DEBUG test1 <> - This is not a NDC log!

② 支持自定义格式

其实这种方法与第①种就是TTCCLayout与PatternLayout的区别。

log4cplus::tstring pattern =  LOG4CPLUS_TEXT("NDC:[%x] - %m %n");
appender->setLayout(std::unique_ptr<Layout>(new PatternLayout(pattern)));
...
NDC& ndc = log4cplus::getNDC();
ndc.push("ur ndc  string");
LOG4CPLUS_DEBUG(logger, "This is a NDC log!");
...
ndc.pop();
LOG4CPLUS_DEBUG(logger, "This is not a NDC log!");
ndc.remove();

输出日志:

NDC:[ur ndc string] - This is a NDC log!
NDC:[] - This is not a NDC log!

③ 直接使用现有的NDCContextCreator

这种方法在其他线程中就不需要显示调用push和pop,所以当出现异常时不用关注两者是否相配对。

NDCContextCreator _first_ndc("ur ndc string");
LOG4CPLUS_DEBUG(logger, "this is a NDC test");

输出日志:

NDC:[ur ndc string] - This is not a NDC log!

示例程序:

#include <iomanip>
#include "log4cplus/logger.h"
#include "log4cplus/consoleappender.h"
#include "log4cplus/loglevel.h"
#include "log4cplus/loggingmacros.h"
#include "log4cplus/initializer.h"
#include "log4cplus/ndc.h"

using namespace std;
using namespace log4cplus;

int main(int argc, char **argv)
{
    log4cplus::Initializer initializer;
    
    SharedAppenderPtr appender(new ConsoleAppender());
    appender->setName(LOG4CPLUS_TEXT("append_1"));
    
    log4cplus::tstring pattern =  LOG4CPLUS_TEXT("NDC:[%x] - %m %n");
	appender->setLayout(std::unique_ptr<Layout>(new PatternLayout(pattern)));

    Logger logger = Logger::getInstance(LOG4CPLUS_TEXT("test1"));
    logger.addAppender(appender);
    
    /* NDC日志测试 */
	NDC& ndc = log4cplus::getNDC();
    ndc.push("ur ndc  string");
    LOG4CPLUS_DEBUG(logger, "This is a NDC log!");
    ndc.pop();

    LOG4CPLUS_DEBUG(logger, "This is not a NDC log!");
    ndc.remove();

	return 0;   
}

输出日志:

NDC:[ur ndc  string] - This is a NDC log!
NDC:[] - This is not a NDC log!

参考文章:

  log4cplus 使用方法 配置 - wclhjs.OSCHINA
  log4cplus 使用方法 配置 - NBA_1.CSDN

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

R-QWERT

你的鼓励是我最大的动力!

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值