Apache log4cxx在C++多进程多线程下的使用
- 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
- 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
- 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。
Apache log4cxx在C++多进程多线程下的使用
1、Apache log4cxx介绍
Apache log4cxx是Apache Logging Services三个日志记录项目之一,完全开源组件。
是著名的日志记录组件log4j的c++移植版,用于为C++程序提供日志功能,以便开发者对目标程序进行调试和审计。
当前的最新版本为0.10.0。
2、Apache log4cxx 框架组成
Apache Log4cxx有三个关键组件,它们是loggers, appenders和layouts。
执行日志操作Logger是log4cxx的核心类。
looger有层次结构,最顶层为RootLogger;logger是分七个级别,分别是debug、info、warn、error、fatal、all、off,最常用的应该是debug()和info();而warn()、error()、fatal()仅在相应事件发生后才使用。
每个logger可以附加多个Appender。
Appender
代表了日志输出的目标,如输出到文件、控制台,数据库等等。
对于每一种appender,都可以通过layout进行格式设置,根据自己需求定制不同日志内容。
使用中用到的类有BasicConfigurator、PropertyConfigurator、DOMConfigurator等,用于对log4cxx进行配置。
其中BasicConfigurator提供了一种简单配置,包括使用ConsoleAppder作为root appender和PatternLayout 作为缺省布局,
PropertyConfigurator使用properties文件作为配置方式,DOMConfigurator则使用properties文件作为配置方式,具体配置文档信息请查阅相关资料。
3、多进程多线程使用设计
Apache Log4cxx 提供的常用供日志调用方法,logger->info(),logger->debug(),logger->warn(),logger->error(),与上述方法类似的还有相应的宏调用LOG4CXX_DEBUG()、LOG4CXX_INFO()、LOG4CXX_WARN()、LOG4CXX_ERROR()。
每个方法与宏的参数要求是全字符串类型,对于不同的日志信息相应调用不同的日志记录方法,即可得到不同级别、不同类型的日志信息。
配置文件的设置Apache Log4cxx提供
static void configure(helpers::Properties& properties)函数,参数中传入配置文件的绝对路径或是相对路径、文件名,日志文件名称 Apache Log4cxx 提供
static LoggerPtr getLogger(const std::wstring& name);
参数中传入日志文件名,用日志文件名称实例化LoggerPtr对象
LoggerPtr logger(Logger::getLogger(trace)),即可用通过logger对象调用相应类型日志方法,宏调用也在实例化日志对象后才能对设置信息有作用大家看到了,方法参数要求是一个字符串类型,不便于C++记录日志,也不习惯于C++程序员使用。
配置文件与日志文件名设备,都用到了静态方法,多线程共用一个日志对象,每个线程一个日志文件,上述静态方法是不能满足这个需求的,多个进程里面不用的线程调用此方法,每个线程一个日志文件,就更不能满足需求,所以我们必须自己设计一种结构来封装Apache Log4cxx提供的方法,满足多进程多线程下,一个线程一个日志文件的需求。
设计一种方便的参数传入模式,使C++程序员能方便使用日志组件提供方法。
我的设计模型如下,用动态库封装日志方法,不同进程、不同线程实例化一
个动态库对象,便能解决多进程,多线程生成不同文件问题,用类似sprintf()格式化参数,封装日志方法参数,当然也可以用特定格式来规定日志输出,便能解决方便操作问题,实例化封装类对象,为了不再引入Apache Log4cxx头文件信息,我提供两个额外的创建实例,销毁实例方法,这样一个完美的日志服务组件就做成了,下面我们来实现这个设想。
4、多进程多线程使用实现
4.1在microsoft Visual C++ 6.0 IDE 上新建一个Win32 Dynamic-Link Library 工程,命名为Trace,选择A simple Dll project,点击Finish一个简单的动态连接库工程就建好了。
4.2 在StdAfx.h文件中包函调用Apache Log4cxx的头文件
#include "logger.h"
#include "PropertyConfigurator.h"
using namespace std;
using namespace log4cxx;
相应的头文件,动态连接库,静态连接库,部分参考文档,请到Apache Log4cxx 官方网址/下载,官方网址只提供工程文件,相应的动态连接库,静态连接库,需求自己编译得到,具体的编译方法请查阅相关文档。
4.3在工程Trace中新增加一个基础类,命名为CBase用于实现Apache Log4cxx 的配置信息,格式化日志文件的输出信息等作用。
Base .h:
#define LOGNAMELNET 100\
class CBase
{
public:
CBase(char * PropertyPath,char * TraceName);
~CBase();
public:
//格式化日志三角输出框
void f_foursquare_sign(char * arg,char *dst);
//格式化日志四角输出框
void f_triangle_sign(char * arg,char *dst);
public:
//日志对象
LoggerPtr m_log;
//日志文件名
char f_c_logname[LOGNAMELNET];
};
Base .cpp:
CBase::CBase(char * PropertyPath,char * TraceName)
{
log4cxx::PropertyConfigurator::configure(PropertyPath);
//configure file
this->m_log = Logger::getLogger(TraceName); //appender
//保存日志名称
memset(this->f_c_logname,0,sizeof(this->f_c_logname));
strcpy(this->f_c_logname,TraceName);
}
CBase::~CBase()
{
}
void CBase::f_triangle_sign(char * arg,char *dst)
{
char c_str[100*2];
memset(c_str,0,sizeof(c_str));
strcat(c_str,"<");
strcat(c_str,arg);
strcat(c_str,">-");
strcat(dst,c_str);
}
void CBase::f_foursquare_sign(char * arg,char *dst)
{
char c_str[100*2];
memset(c_str,0,sizeof(c_str));
strcat(c_str,"[");
strcat(c_str,arg);
strcat(c_str,"]-");
strcat(dst,c_str);
}
4.4 增加一个接口类CTraceImpl,向调用者提供接口,这个类只声明接口虚函数,可以直接在项目头文件中实现,额外的创建对象,销毁对象方法也在此声明,实现直接放在项目文件中。
Trace.h:
#ifndef __TRACEIMPL__
#define __TRACEIMPL__
//动态库输出调用约定
#define TRACEFUN __declspec(dllexport)
class TRACEFUN CTraceImpl
{
public:
//重设日志文件名称
virtual void ResetTraceName(char * TraceName) = 0;
public:
//输出Debug日志
virtual void DebugLog(char * CallName,int LogSign,char * StrMsg,...) = 0;
//输出Info日志
virtual void InfoLog (char * EventName,int LogSign,char * StrMsg,...) = 0;
……
};
//创建实例
TRACEFUN CTraceImpl * CreateTrace(char * PropertyPath = "./log4cxx.properties",char * TraceName = "defilename");
//销毁实例
TRACEFUN void DestroyTrace(CTraceImpl * pCTrace);
#endif // #ifndef __TRACEIMPL__
Trace.cpp:
//创建实例
TRACEFUN CTraceImpl * CreateTrace(char * PropertyPath ,char * TraceName ) {
//创建新对象
return ( new CTrace(PropertyPath,TraceName) );
}
//销毁实例
TRACEFUN void DestroyTrace(CTraceImpl * pCTrace)
{
if (pCTrace)
{
//销毁对象
CTraceImpl* fp = (CTraceImpl *)pCTrace;
delete fp;
}
}
4.5 新增一个实现类CTrace,从CBase, CTraceImpl继承,实现日志方法的封装,参数的格式化:
_Trace.h:
class CTrace:public CBase,public CTraceImpl
{
public:
//默认配置文件名、日志文件名
CTrace(char * PropertyPath ,char * TraceName );
~CTrace();
public:
//重设日志文件名称
virtual void ResetTraceName(char * TraceName);
public:
//输出Debug日志
virtual void DebugLog(char * CallName,int LogSign,char * StrMsg,...);
//输出Info日志
virtual void InfoLog (char * Name,int LogSign,char * StrMsg,...);
……
};
_Trace.cpp:
#define MAXARRAYLEN 1024*2
//日志标志
#define FUN_BEGIN "BEGIN"
#define FUN_END " END "
//日志类型
#define TYPE_LOG " THREAD "
CTrace::CTrace(char * PropertyPath,char * TraceName):CABBase(PropertyPath,TraceName)
{
}
CTrace::~CTrace()
{
void CTrace::InfoLog(char * Name, int LogSign, char *StrMsg, ...) {
char c_str[MAXARRAYLEN],c_join_str[MAXARRAYLEN];
try
{
//设置日志文件名称
this->m_log = Logger::getLogger(this->f_c_logname);
memset(c_join_str,0,sizeof(c_join_str));
memset(c_str,0,sizeof(c_str));
//日志类型
this->f_triangle_sign(TYPE_LOG,c_join_str);
//调用方法
this->f_foursquare_sign(Name,c_join_str);
//日志标志
if (LogSign > 0)
{
this->f_triangle_sign(LogSign < 2?FUN_BEGIN:FUN_END,c_join_str);
}
//格式化参数
va_list ap;
va_start ( ap, StrMsg );
vsprintf(c_str,StrMsg,ap);
va_end ( ap );
strcat(c_join_str,c_str);
//打印日志
this->m_log->info(c_join_str);
}
catch (...)
{
//异常处理
strcat(c_join_str,"Format parameter error!!!");
this->m_log->info(c_join_str);
}
}
void CTrace::DebugLog(char *CallName, int LogSign, char *StrMsg, ...) {
char c_str[MAXARRAYLEN],c_join_str[MAXARRAYLEN];
try
{
//设置日志文件名称
this->m_log = Logger::getLogger(this->f_c_logname);
memset(c_join_str,0,sizeof(c_join_str));
memset(c_str,0,sizeof(c_str));
//日志类型
this->f_triangle_sign(TYPE_LOG,c_join_str);
//调用方法
this->f_foursquare_sign(CallName,c_join_str);
if (LogSign > 0)
{
this->f_triangle_sign(LogSign < 2?FUN_BEGIN:FUN_END,c_join_str);
}
//格式化参数
va_list ap;
va_start ( ap, StrMsg );
vsprintf(c_str,StrMsg,ap);
va_end ( ap );
strcat(c_join_str,c_str);
//打印日志
this->m_log->debug(c_join_str);
}
catch (...)
{
//异常处理
strcat(c_join_str,"Format parameter error!!!");
this->m_log->debug(c_join_str);
}
}
void CTrace::ResetTraceName(char *TraceName)
{
//设置日志名称
strcpy(this->f_c_logname,TraceName);
}
这只实现了Info,debug接口函数,其它方法同理,同一方法也可以以不同的形式实现,对Apache Log4cxx 本实现调用的是成员方法,也可以用宏实现。
4.6 现在编译就可以生成你需要的在多进程多线程下应用的日志组件了,调用时
用CreateTrace ()实例化日志对象,用DestroyTrace()销毁对象,
5、设想与展望
Apache Log4cxx 的日志函数是基于字符串形式的,设计时是否基于安全考虑?对于新版本如能实现格式化参数,就不用程序员封装了。
Apache Log4cxx 日志记录方式是基于时间段回滚机制,例:以天为单位记录日志,日志首先记录到一个指定文件中,到晚上十二点整,把指定文件内容转移到以前一天日期命名的日志文件中,接下来的日志继续输出到指定文件中,以此递进。
这种模式对于服务器端是完全适应的,但对那些不是全天候运行的程序,就不能满足每天一个文件的需求。