一、配置文件的例子
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
[[email protected] test1]# autoscan [[email protected] test1]# ls aLogger.cpp aLogger.h autoscan.log configure.scan main.cpp [[email protected] test1]# mv configure.scan configure.in
b) 编辑configure.in文件
[[email protected] test1]# vim configure.in AC_PREREQ([2.63]) AC_INIT([MyfirstPro], [1.0], [[email protected]]) 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文件
[[email protected] test1]# autoheader [[email protected] test1]# ls aLogger.cpp aLogger.h autom4te.cache autoscan.log config.h.in configure.in main.cpp
d) 运行aclocal,生成aclocal.m4文件
[[email protected] test1]# aclocal [[email protected] test1]# ls aclocal.m4 aLogger.cpp aLogger.h autom4te.cache autoscan.log config.h.in configure.in main.cpp
e) 运行autoconf,生成configure脚本
[[email protected] test1]# autoconf [[email protected] test1]# ls aclocal.m4 aLogger.cpp aLogger.h autom4te.cache autoscan.log config.h.in configure configure.in main.cpp
f) 编写Makefile.am脚本
[[email protected] 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 [[email protected] 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
[[email protected] test1]# libtoolize [[email protected] test1]# automake -a [[email protected] 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文件
[[email protected] test1]# ./configure #开始编译我们的程序 [[email protected] 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‘ #运行测试 [[email protected] test1]# ./firstPro 20150719-09:23:32 logicserver DEBUG: 定时测试, 字符串test, 数字123456789 20150719-09:23:32 logicserver ERROR: 条件测试, 条件满足则输出 字符串test
六、动态编译和静态编译
动态编译的可执行文件需要附带一个的动态链接库,在执行时,需要调用其对应动态链接库中的命令。所以其优点一方面是缩小了执行文件本身的体积,另一方面是加快了编译速度,节省了系统资源。缺点一是哪怕是很简单的程序,只用到了链接库中的一两条命令,也需要附带一个相对庞大的链接库;二是如果其他计算机上没有安装对应的运行库,则用动态编译的可执行文件就不能运行。
静态编译就是编译器在编译可执行文件的时候,将可执行文件需要调用的对应动态链接库(.so)中的部分提取出来,链接到可执行文件中去,使可执行文件在运行的时候不依赖于动态链接库。所以其优缺点与动态编译的可执行文件正好互补。
a)动态编译
[[email protected] 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) [[email protected] test1]# du -sh firstPro 316K firstPro #这个可执行文件的大小
从上面make编译时的输出,和这里的ldd的输出,我们可以确定,这个程序是动态链接,也就是这个程序运行的服务器上,必须安装了log4cxx的库才行。
[[email protected] ~]$ ./firstPro ./firstPro: error while loading shared libraries: liblog4cxx.so.10: cannot open shared object file: No such file or directory
b)修改la文件
[[email protected] test1]# vim /usr/local/log4cxx/lib/liblog4cxx.la [[email protected] test1]# vim /usr/local/apr/lib/libapr-1.la [[email protected] test1]# vim /usr/local/apr-util/lib/libaprutil-1.la [[email protected] test1]# vim /usr/local/apr-util/lib/libexpat.la #将这4个文件的dlname和library_names这2行注释掉
c)静态编译
[[email protected] test1]# make clean [[email protected] test1]# make #将文件大小和链接库和上面的对比下 [[email protected] test1]# du -sh firstPro 12M firstPro [[email protected] 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的库,可以在没有安装它的环境下运行。
#让程序在后台运行,并指定输出日志 [[email protected] ~]$ ./firstPro -d -l /log/out.log
这就是log4cxx简单的文本输出和控制台输出的类。