一、配置文件的例子
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。
五、搭建编译环境
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简单的文本输出和控制台输出的类。
转载于:https://blog.51cto.com/svenman/1676588