下载地址:
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
#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;
#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格式控制,主要包括以下标识符:
标识符 | 说明 |
---|---|
%% | 转义为% |
%c | logger名称 |
%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 | 嵌套诊断上下文 |
-数值 | 左对齐 |
#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;
}
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()...
在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
三种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;
}
配置文件(自定义名字为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!
#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;
}
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;
}
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