一、配置文件的例子

log4j.rootLogger=DEBUG,lib
log4j.appender.lib=org.apache.log4j.ConsoleAppender
log4j.appender.lib.Threshold=DEBUG
log4j.appender.lib.File=./log/output.log
log4j.appender.lib.Append=true
log4j.appender.lib.layout=org.apache.log4j.PatternLayout
log4j.appender.lib.layout.ConversionPattern=[%-5p] %d %l : %m%n

下面就是一个简单的用配置文件配置log4cxx的代码

#include <log4cxx/logger.h>
#include <log4cxx/basicconfigurator.h>
#include <log4cxx/propertyconfigurator.h>
#include <log4cxx/helpers/exception.h>
#include <iostream>
int main()
{
        log4cxx::PropertyConfigurator::configureAndWatch("log4cxx.properties");
        log4cxx::LoggerPtr logger(log4cxx::Logger::getLogger("lib"));
        LOG4CXX_DEBUG(logger, "this is log4cxx test");
        return 0;
}

用配置文件的例子,网上可以找到很多,我这里要写的是用代码直接配置log4cxx。


二、头文件

#ifndef _aLogger_H__
#define _aLogger_H__

#include <log4cxx/logger.h>
#include <log4cxx/logstring.h>
#include <log4cxx/log4cxx.h>

#define _MSGBUF_MAX 4096

class aLogger
{
public:
	aLogger(const log4cxx::LogString &name="undefined");

	~aLogger();

	/**
 	 * @brief 添加控制台日志
 	 */
	void addConsoleLog(const log4cxx::LayoutPtr &layout);

	/**
 	 * @brief 移除控制台日志
 	 */
	void removeConsoleLog();

	/**
 	 * @brief 添加回滚文件日志
 	 */
	void addDailyLocalFileLog(const log4cxx::LayoutPtr &layout);

	/**
 	 * @brief 设置日志等级
 	 *
 	 * @param "TRACE" < "DEBUG" < "INFO" < "WARN" < "ERROR" < "FATAL"
 	 */
	bool setLevel(const log4cxx::LogString &level);
	
	/** 
	 * @brief trace 级别日志
	 * 
	 * @param format 参数
	 * @param ... 参数列表
	 */
	void trace(const char *format, ... );

	void debug(const char *format, ... );

	void info(const char *format, ... );

	void warn(const char *format, ... );

	void error(const char *format, ... );

	void fatal(const char *format, ... );

	/**
 	 * @brief 强制写日志,无视当前的日志级别
 	 */
	void forcelog(const log4cxx::LogString &level, const char *format, ... );

	/**
 	 * @brief 条件写错误日志,无视当前的日志级别
 	 */
	void assertlog(bool condition, const log4cxx::LogString &level, const char *format, ... );
private:
	log4cxx::LoggerPtr logger;
};

#endif


三、实现文件

#include <log4cxx/helpers/pool.h>
#include <log4cxx/basicconfigurator.h>
#include <log4cxx/dailyrollingfileappender.h>
#include <log4cxx/consoleappender.h>
#include <log4cxx/patternlayout.h>
#include <log4cxx/logmanager.h>
#include <cstdarg>
#include <cstdio>
#include "aLogger.h"

using namespace std;
using namespace log4cxx;

extern map<string, string> argMap;

aLogger::aLogger(const LogString &name)
{
	PatternLayoutPtr layout = new PatternLayout();
	LogString conversionPattern = LogString("%d{yyMMdd-HH:mm:ss} ") + name + LogString(" %5p: %m%n");
	layout->setConversionPattern(conversionPattern);

        logger = Logger::getRootLogger();
	logger->setLevel(Level::getTrace());
	addConsoleLog(layout);
	addDailyLocalFileLog(layout);
}

aLogger::~aLogger()
{
	LogManager::shutdown();
}

void aLogger::addConsoleLog(const LayoutPtr &layout)
{
	if (NULL == layout) return;

	helpers::Pool p;

	ConsoleAppenderPtr consoleAppender = new ConsoleAppender(LayoutPtr(layout));
	consoleAppender->setTarget(ConsoleAppender::getSystemOut());
	consoleAppender->activateOptions(p);
	consoleAppender->setEncoding("UTF-8");
	consoleAppender->setName("consoleAppender");

	BasicConfigurator::configure(AppenderPtr(consoleAppender));
}

void aLogger::removeConsoleLog()
{
	if (NULL == logger) return;
	ConsoleAppenderPtr consoleAppender = logger->getAppender("consoleAppender");
	if (NULL == consoleAppender) return;
	if (logger->isAttached(consoleAppender))
		logger->removeAppender(consoleAppender);
}

void aLogger::addDailyLocalFileLog(const LayoutPtr &layout)
{
	if (NULL == layout) return;

        helpers::Pool p;
	
	LogString LogPath;
	map<string, string>::const_iterator it = argMap.find("logPath");
	if (it == argMap.end())
		LogPath = "/tmp/out.log";
	else
		LogPath = it->second;
	
	DailyRollingFileAppenderPtr rollingfileAppender = new DailyRollingFileAppender();
	rollingfileAppender->setFile(LogPath);
        rollingfileAppender->setAppend(true);
        rollingfileAppender->setDatePattern("'.'yyyyMMdd-HH");
        rollingfileAppender->setLayout(LayoutPtr(layout));
        rollingfileAppender->activateOptions(p);
	rollingfileAppender->setEncoding("UTF-8");
	rollingfileAppender->setName("rollingfileAppender");

	BasicConfigurator::configure(AppenderPtr(rollingfileAppender));
}

bool aLogger::setLevel(const std::string &level)
{
	if (NULL == logger) return false;
	
	//如果字符串不符合要求,则返回DEBUG级别的指针
	LevelPtr pLevel = Level::toLevel(level);
        logger->setLevel(pLevel);
	return true;
}

void aLogger::trace(const char *format, ... )
{
	if (NULL == logger) return;

	char message[_MSGBUF_MAX];
	va_list ArgList;
	va_start(ArgList, format);
	vsnprintf(message, sizeof(message), format, ArgList);
	va_end(ArgList);
	LOG4CXX_TRACE(logger, message);
}

void aLogger::debug(const char *format, ... )
{
	if (NULL == logger) return;

	char message[_MSGBUF_MAX];
	va_list ArgList;
	va_start(ArgList, format);
	vsnprintf(message, sizeof(message), format, ArgList);
	va_end(ArgList);
	LOG4CXX_DEBUG(logger, message);
}

void aLogger::info(const char *format, ... )
{
	if (NULL == logger) return;

	char message[_MSGBUF_MAX];
	va_list ArgList;
	va_start(ArgList, format);
	vsnprintf(message, sizeof(message), format, ArgList);
	va_end(ArgList);
	LOG4CXX_INFO(logger, message);
}

void aLogger::warn(const char *format, ... )
{
	if (NULL == logger) return;

	char message[_MSGBUF_MAX];
	va_list ArgList;
	va_start(ArgList, format);
	vsnprintf(message, sizeof(message), format, ArgList);
	va_end(ArgList);
	LOG4CXX_WARN(logger, message);
}

void aLogger::error(const char *format, ... )
{
	if (NULL == logger) return;

	char message[_MSGBUF_MAX];
	va_list ArgList;
	va_start(ArgList, format);
	vsnprintf(message, sizeof(message), format, ArgList);
	va_end(ArgList);
	LOG4CXX_ERROR(logger, message);
}

void aLogger::fatal(const char *format, ... )
{
	if (NULL == logger) return;

	char message[_MSGBUF_MAX];
	va_list ArgList;
	va_start(ArgList, format);
	vsnprintf(message, sizeof(message), format, ArgList);
	va_end(ArgList);
	LOG4CXX_FATAL(logger, message);
}

void aLogger::forcelog(const log4cxx::LogString &level, const char *format, ... )
{
	if (NULL == logger) return;
	
	LevelPtr pLevel = Level::toLevel(level);
	
	char message[_MSGBUF_MAX];
	va_list ArgList;
	va_start(ArgList, format);
	vsnprintf(message, sizeof(message), format, ArgList);
	va_end(ArgList);
	helpers::MessageBuffer oss_;
	logger->forcedLog(pLevel, oss_.str(oss_ << message), LOG4CXX_LOCATION);
}

void aLogger::assertlog(bool condition, const log4cxx::LogString &level, const char *format, ... )
{
	if (NULL == logger || !condition) return;
	
	LevelPtr pLevel = Level::toLevel(level);
	
	char message[_MSGBUF_MAX];
	va_list ArgList;
	va_start(ArgList, format);
	vsnprintf(message, sizeof(message), format, ArgList);
	va_end(ArgList);
	helpers::MessageBuffer oss_;
	logger->forcedLog(pLevel, oss_.str(oss_ << message), LOG4CXX_LOCATION);
}


四、测试程序

#include <unistd.h>
#include <map>
#include <cstdlib>
#include <memory>
#include "aLogger.h"

using namespace std;

map<string, string> argMap;

int main(int argc, char *argv[])
{
	int result = 0;
	opterr = 0;
	while ((result = getopt(argc, argv, "dl:")) != -1)
	{
		switch(result)
		{
			case 'd':
			{
				argMap["daemon"] = "1";
				break;	
			}
			case 'l':
			{
				argMap["logPath"] = optarg;
				break;
			}
		}
	}
	auto_ptr<aLogger> logger(new aLogger("logicserver"));
	logger->setLevel("TRACE");
	if (atoi(argMap["daemon"].c_str()) == 1)
	{
		logger->removeConsoleLog();
		daemon(1, 1);
	}
	while (true)
        {
                logger->debug("定时测试, 字符串%s, 数字%d", "test", 123456789);
		logger->assertlog(1 == 1, "ERROR", "条件测试, 条件满足则输出 字符串%s", "test");
                sleep(1);
        }
	return 0;
}

说明:

a) daemon函数让程序后台运行

int daemon(int nochdir, int noclose);

参数:

当 nochdir为零时,当前目录变为根目录,否则不变;

当 noclose为零时,标准输入、标准输出和错误输出重导向为/dev/null,也就是不输出任何信 息,否则照样输出。

返回值:

deamon()调用了fork(),如果fork成功,那么父进程就调用_exit(2)退出,所以看到的错误信息 全部是子进程产生的。如果成功函数返回0,否则返回-1并设置errno。


五、搭建编译环境

log4cxx配置使用(二)_include

a)首先执行autoscan

[root@oracle test1]# autoscan 
[root@oracle test1]# ls
aLogger.cpp  aLogger.h  autoscan.log  configure.scan  main.cpp
[root@oracle test1]# mv configure.scan configure.in

b) 编辑configure.in文件

[root@oracle test1]# vim configure.in
AC_PREREQ([2.63])
AC_INIT([MyfirstPro], [1.0], [1245366017@qq.com])
AC_CONFIG_SRCDIR([aLogger.h])
AC_CONFIG_HEADERS([config.h])
#添加automake
AM_INIT_AUTOMAKE(MyfirstPro,1.0)

# Checks for programs.
AC_PROG_CXX
AC_PROG_CC
#添加libtool工具
AC_PROG_LIBTOOL
.
.
.

#修改这一行,输出Makefile
AC_OUTPUT(Makefile)

c) 运行autoheader,生成config.h.in文件

[root@oracle test1]# autoheader 
[root@oracle test1]# ls
aLogger.cpp  aLogger.h  autom4te.cache  autoscan.log  config.h.in  configure.in  main.cpp

d) 运行aclocal,生成aclocal.m4文件

[root@oracle test1]# aclocal
[root@oracle test1]# ls
aclocal.m4  aLogger.cpp  aLogger.h  autom4te.cache  autoscan.log  config.h.in  configure.in  main.cpp

e) 运行autoconf,生成configure脚本

[root@oracle test1]# autoconf 
[root@oracle test1]# ls
aclocal.m4  aLogger.cpp  aLogger.h  autom4te.cache  autoscan.log  config.h.in  configure  configure.in  main.cpp

f) 编写Makefile.am脚本

[root@oracle test1]# vim Makefile.am 
AUTOMAKE_OPTIONS = foreign
INCLUDES = -I/usr/include/log4cxx/
bin_PROGRAMS = firstPro
firstPro_SOURCES = \
                aLogger.cpp \
                main.cpp
firstPro_LDADD = /usr/local/log4cxx/lib/liblog4cxx.la
firstPro_LDFLAGS = -D_GNU_SOURCE
[root@oracle test1]# ls
aclocal.m4   aLogger.h       autoscan.log  configure     main.cpp
aLogger.cpp  autom4te.cache  config.h.in   configure.in  Makefile.am

g) 运行automake,生成Makefile.in

[root@oracle test1]# libtoolize
[root@oracle test1]# automake -a
[root@oracle test1]# ls
aclocal.m4   aLogger.h       autoscan.log  config.h.in  configure     depcomp     ltmain.sh  Makefile.am  missing
aLogger.cpp  autom4te.cache  config.guess  config.sub   configure.in  install-sh  main.cpp   Makefile.in

h) 执行./configure脚本,生成Makefile文件

[root@oracle test1]# ./configure 
#开始编译我们的程序
[root@oracle test1]# make 
(CDPATH="${ZSH_VERSION+.}:" && cd . && /bin/sh /root/test1/missing --run autoheader)
rm -f stamp-h1
touch config.h.in
cd . && /bin/sh ./config.status config.h
config.status: creating config.h
make  all-am
make[1]: Entering directory `/root/test1'
g++ -DHAVE_CONFIG_H -I. -I/usr/include/log4cxx/    -g -O2 -MT aLogger.o -MD -MP -MF .deps/aLogger.Tpo -c -o aLogger.o aLogger.cpp
mv -f .deps/aLogger.Tpo .deps/aLogger.Po
g++ -DHAVE_CONFIG_H -I. -I/usr/include/log4cxx/    -g -O2 -MT main.o -MD -MP -MF .deps/main.Tpo -c -o main.o main.cpp
mv -f .deps/main.Tpo .deps/main.Po
/bin/sh ./libtool --tag=CXX   --mode=link g++  -g -O2 -D_GNU_SOURCE  -o firstPro aLogger.o main.o /usr/local/log4cxx/lib/liblog4cxx.la 
libtool: link: g++ -g -O2 -D_GNU_SOURCE -o firstPro aLogger.o main.o  /usr/local/log4cxx/lib/liblog4cxx.so -L/usr/local/apr/lib -L/usr/local/apr-util/lib /usr/local/apr-util/lib/libaprutil-1.so /usr/local/apr-util/lib/libexpat.so /usr/local/apr/lib/libapr-1.so -lrt -lcrypt -lpthread -pthread -Wl,-rpath -Wl,/usr/local/apr-util/lib -Wl,-rpath -Wl,/usr/local/apr/lib -Wl,-rpath -Wl,/usr/local/apr-util/lib -Wl,-rpath -Wl,/usr/local/apr/lib
make[1]: Leaving directory `/root/test1'

#运行测试
[root@oracle test1]# ./firstPro 
20150719-09:23:32 logicserver DEBUG: 定时测试, 字符串test, 数字123456789
20150719-09:23:32 logicserver ERROR: 条件测试, 条件满足则输出 字符串test


六、动态编译和静态编译

动态编译的可执行文件需要附带一个的动态链接库,在执行时,需要调用其对应动态链接库中的命令。所以其优点一方面是缩小了执行文件本身的体积,另一方面是加快了编译速度,节省了系统资源。缺点一是哪怕是很简单的程序,只用到了链接库中的一两条命令,也需要附带一个相对庞大的链接库;二是如果其他计算机上没有安装对应的运行库,则用动态编译的可执行文件就不能运行

静态编译就是编译器在编译可执行文件的时候,将可执行文件需要调用的对应动态链接库(.so)中的部分提取出来,链接到可执行文件中去,使可执行文件在运行的时候不依赖于动态链接库。所以其优缺点与动态编译的可执行文件正好互补。


a)动态编译

[root@oracle test1]# ldd firstPro 
	linux-vdso.so.1 =>  (0x00007fff0c7ff000)
	liblog4cxx.so.10 => /usr/local/log4cxx/lib/liblog4cxx.so.10 (0x00007f17d01a8000)
	libaprutil-1.so.0 => /usr/local/apr-util/lib/libaprutil-1.so.0 (0x00007f17cff82000)
	libexpat.so.0 => /usr/local/apr-util/lib/libexpat.so.0 (0x00007f17cfd5b000)
	libapr-1.so.0 => /usr/local/apr/lib/libapr-1.so.0 (0x00007f17cfb28000)
	librt.so.1 => /lib64/librt.so.1 (0x0000003c54200000)
	libcrypt.so.1 => /lib64/libcrypt.so.1 (0x0000003c5d600000)
	libpthread.so.0 => /lib64/libpthread.so.0 (0x0000003c53600000)
	libstdc++.so.6 => /usr/lib64/libstdc++.so.6 (0x0000003c60a00000)
	libm.so.6 => /lib64/libm.so.6 (0x0000003c53e00000)
	libgcc_s.so.1 => /lib64/libgcc_s.so.1 (0x0000003c5ee00000)
	libc.so.6 => /lib64/libc.so.6 (0x0000003c53200000)
	/lib64/ld-linux-x86-64.so.2 (0x0000003c52a00000)
	libfreebl3.so => /lib64/libfreebl3.so (0x0000003c5de00000)
	libdl.so.2 => /lib64/libdl.so.2 (0x0000003c52e00000)
[root@oracle test1]# du -sh firstPro 
316K	firstPro    #这个可执行文件的大小

从上面make编译时的输出,和这里的ldd的输出,我们可以确定,这个程序是动态链接,也就是这个程序运行的服务器上,必须安装了log4cxx的库才行。

[root@test ~]$ ./firstPro
./firstPro: error while loading shared libraries: liblog4cxx.so.10: cannot open shared object file: No such file or directory


b)修改la文件

[root@oracle test1]# vim /usr/local/log4cxx/lib/liblog4cxx.la 
[root@oracle test1]# vim /usr/local/apr/lib/libapr-1.la
[root@oracle test1]# vim /usr/local/apr-util/lib/libaprutil-1.la
[root@oracle test1]# vim /usr/local/apr-util/lib/libexpat.la 
#将这4个文件的dlname和library_names这2行注释掉


c)静态编译

[root@oracle test1]# make clean
[root@oracle test1]# make

#将文件大小和链接库和上面的对比下
[root@oracle test1]# du -sh firstPro 
12M	firstPro
[root@oracle test1]# ldd firstPro 
	linux-vdso.so.1 =>  (0x00007fffbb1ff000)
	librt.so.1 => /lib64/librt.so.1 (0x0000003c54200000)
	libcrypt.so.1 => /lib64/libcrypt.so.1 (0x0000003c5d600000)
	libpthread.so.0 => /lib64/libpthread.so.0 (0x0000003c53600000)
	libstdc++.so.6 => /usr/lib64/libstdc++.so.6 (0x0000003c60a00000)
	libm.so.6 => /lib64/libm.so.6 (0x0000003c53e00000)
	libgcc_s.so.1 => /lib64/libgcc_s.so.1 (0x0000003c5ee00000)
	libc.so.6 => /lib64/libc.so.6 (0x0000003c53200000)
	/lib64/ld-linux-x86-64.so.2 (0x0000003c52a00000)
	libfreebl3.so => /lib64/libfreebl3.so (0x0000003c5de00000)
	libdl.so.2 => /lib64/libdl.so.2 (0x0000003c52e00000)

这样就不需要链接log4cxx的库,可以在没有安装它的环境下运行。

#让程序在后台运行,并指定输出日志
[root@test ~]$ ./firstPro -d -l /log/out.log

这就是log4cxx简单的文本输出和控制台输出的类。