一、简单介绍
ACE日志系统也具有线程安全、灵活、分级显示等特点,可以面向程序调试、运行、测试、和维护等全生命周期,可以选择将信息输出到屏幕、文件、系统日志(如Windows下的Event log)、甚至是远程服务器。除此之外,ACE日志系统支持回调函数以及运行时刻动态配置(Runtime Configuration )。
1.1 log输出目的地与格式:
输出目的地:就是我们要把日志信息输出或者打印到什么地方,比如控制台的屏幕、文本文件或者syslog系统日志文件等。
输出格式: 在用户提交log信息时,是否在用户提交的日志信息前面插入一些标记,比如程序名称、线程id,提交日志消息时的时间、日志优先级等额外的信息。
STDERR 把日志消息输出到标准错误输出流、也就是stderr.
LOGGER 输出到本地的logger守卫程序
OSTREAM 把日志输出到ostream *对象指定的地方。比如可以使用ofstrem指定输出到某个文件。必须调用 msg_ostream成员函数设置一个ostream *对象
MSG_CALLBACK 把日志输出到回调对象。必须自己定义一个从ACE_Log_Msg_Callback继承的子类,并实现新类的log函数和调用ACE_Log_Msg 的msg_callback函数设置一个 ACE_Log_Msg_Callback子类的对象指针。
VERBOSE 在日志消息中记录日志信息的程序名称、线程id、日志记录时间等。默认情况下是不会输出记录日志的时间的,可以通过设置系统环境变量ACE_LOG_TIMESTAMP来输出时间的格式。可以为TIME或者DATE。
VERBOSE_LITE 打印一些是线程相关的信息。
SILENT 不输出任何日志消息、即使设置了STDERR、LOGGER 等输出格式时。
SYSLOG 把日志消息输出到本地系统日志模块。 如 linux的sysylog或者windows的事件 /// Write messages to the user provided backend
CUSTOM 与MAG_CALLBACK类似。必须定义一个从ACE_Log_Msg_Backend继承的子类,并实现新类的log接口。和调用ACE_Log_Msg的msg_backend静态函数设置ACE_Log_Msg_Backend的一个实现的指针。 该标志与MSG_CALLBACK不同的是针对系统当前的所有ACE_Log_Msg都会进行该设置,而MSG_CALLBACK只是设置当前的ACE_Log_Msg对象。
log输出目的地:STDERR 、LOGGER 、OSTREAM 、MSG_CALLBACK 、SYSLOG 、CUSTOM
log输出的附加格式:VERBOSE 、VERBOSE_LITE
set_flags (u_long f) 设置log的输出目的地或格式,可以同时设置多个标志。
clr_flags (u_long f); 取消某个log输出目的地或者格式。
1.2 log输出优先级:
enum ACE_Log_Priority
{
LM_SHUTDOWN = 01,
LM_TRACE = 02,
LM_DEBUG = 04,
LM_INFO = 010,
LM_NOTICE = 020,
LM_WARNING = 040,
LM_STARTUP = 0100,
LM_ERROR = 0200,
LM_CRITICAL = 0400,
LM_ALERT = 01000,
LM_EMERGENCY = 02000,
LM_MAX = LM_EMERGENCY,
LM_ENSURE_32_BITS = 0x7FFFFFFF
};
与其说是log输出优先级还不如说他们是log输出的类型。他们在log中都是优先级的标志位来判断是否设定某个输出类型的。
static void disable_debug_messages (ACE_Log_Priority priority = LM_DEBUG); 取消输出某个优先级的日志消息
static void enable_debug_messages (ACE_Log_Priority priority = LM_DEBUG); 使某个优先级的日志消息可以被输出。
1.3 日志输出宏及控制
ACE程序中我们会使用一下几个常用的宏来输出我们的日志消息:
ACE_HEX_DUMP(X) 以16禁止的格式出书X的内容
ACE_RETURN(Y)
ACE_ERROR_BREAK(X)
ACE_ERROR(X)
ACE_DEBUG(X) # 我们最常用的。
ACE_ERROR_INIT(VALUE, FLAGS)
编译时支持日志输出:默认情况下ACE在编译时是支持以上几个宏输出日志消息的。如果不想在自己的程序中输出日志消息时,可以定义宏ACE_NLOGGING ,这样上面所有的宏都会变成一个空的do while语句,不执行任何内容。
运行时支持日志输出:
如果我们不做任何设置的话,那么我们的程序默认是把日志输出到STDERR的。一般的控制台应用程序已经可以满足。
在很多情况下,比如和MFC一起使用时,或者我们想把日志记录到文件时我们就需要重新设置log的输出目的地,通过set_flags/clr_flags设置程序指定的日志输出目的地。
多线程日志输出控制:
ACE_DEBUG等几个预定义的宏所使用的ACE_Log_Msg对象虽然是通过ACE_Log_Msg::instance接口获得的,但是并以意味这整个程序中只有一个ACE_Log_Msg对象。在ACE中如果我们编译的库是属于多线程安全的(一定是多线程安全的,因为我们根本就不会编译,不会基于ACE开发一个单线程的程序)那么对于每一个线程都会有一个ACE_Log_Msg对象。也就是不同的线程在使用ACE_DEBUG输出日志消息时所使用的ACE_Log_Msg对象是不一样的。因此当我们把日志的输出目的或者格式改变而不是使用默认的输出目的地和格式时,我们在每一个线程都需要设置每一个线程所属的ACE_Log_Msg对象的日志消息输出格式。
例外:如果我们的输出目的地是使用CUSTOM 的话,那么我们就不需要设置每一个线程的输出目的地了。因为CUSTOM 的设置是对当前进程的所有线程都生效的。
注意:
如果用户使用CUSTOM 或者MSG_CALLBACK 时,如果在实现的接口log中不加锁时,存在多个线程同时打印输出日志消息的情况。这样就很可能出现2条日志消息在输出的时候交叉在一起。此时我们可以对log函数输出进行加锁。但是加锁的时候,由于log打印或者输出消息到文件或者某些I/O口时,其时间来说相对比较长,这样程序的性能就会比较差劲。
建议:
ACE_Log_Msg_Callback或者ACE_Log_Msg_Backend的子类同时继承自ACE_TASK,log接口只实现copy消息,并把消息放入线程专有的消息对了中,由svc接口获取日志信息在打印。也就是在我们的程序中提供一个专门接收日志信息的缓冲队列并输出的线程。
二、演示例子:
/**/
/**
* 演示ACE_Log_Msg类的使用
*
* ACE日志系统几乎提供了应用程序所需要的全部日志功能,尤其对于
* ACE_Log_Msg_Backend提供了必要的接口将日志分门别类(logger_key)
* 的输出到不同设备(如:屏幕、文件、日志服务器Deamon等),这对于同一
* 模块中对不同逻辑(如:内存,线程等资源的使用)的监测提供了便利。
*
* 理解TRACE(ACE_TRACE)和调试(ACE_DEBUG)信息的含义:
* -ACE_TRACE
* 跟踪函数的调用堆栈(通过tab缩进表示)——观察对象的生命
* 周期等非常有用。
* 通过在编译器中加入或删除ACE_NTRACE=0预定义宏灵活控制信息
* 输出与否。
*
* -ACE_DEBUG
* 主要用于监测程序逻辑数据的正确性。
*
* !!!注意!!!
* 不要在ACE_Log_Msg_Callback或ACE_Log_Msg_Backend的子类中调用
* ACE_TRACE和ACE_DEBUG等宏输出信息。
*/
#include
"
ace/OS.h
"
#include
"
ace/Log_Msg.h
"
#include
"
ace/Log_Record.h
"
#include
"
ace/Log_Msg_Callback.h
"
#include
"
ace/Log_Msg_Backend.h
"
#include
<
string
>
#include
<
fstream
>
//
*******************************************************************************
const
ACE_TCHAR
*
prog_name
=
ACE_TEXT(
"
ACE_Log_Msg_test
"
);
const
ACE_TCHAR
*
logger_key
=
ACE_TEXT(
"
ACE_Log_Msg_test*
"
);
//
*******************************************************************************
class
My_Log_Msg_Callback :
public
ACE_Log_Msg_Callback
...
{
ACE_Log_Msg_Callback *oldcallback_;
public:
~My_Log_Msg_Callback()
...{
ACE_OS::printf(ACE_TEXT("My_Log_Msg_Callback::~My_Log_Msg_Callback() "));
// !!!注意销毁后清理ACE_Log_Msg相应状态,ACE_Log_Msg作为
// Singleton形式存在,系统退出时ACE_Object_Manager清理它。
// 这里假设My_Log_Msg_Callback实例生命周期短于ACE_Log_Msg实例,
// 并非所有情况都是正确的
ACE_LOG_MSG->clr_flags(ACE_Log_Msg::MSG_CALLBACK);
ACE_LOG_MSG->msg_callback(oldcallback_);
}
void oldcallback(const ACE_Log_Msg_Callback *backend)
...{
oldcallback_ = const_cast<ACE_Log_Msg_Callback *>(backend);
}
virtual void log (ACE_Log_Record &log_record)
...{
ACE_OS::printf("[Log_Msg_Callback]%s", log_record.msg_data());
}
}
;
class
My_Log_Msg_Backend :
public
ACE_Log_Msg_Backend
...
{
ACE_Log_Msg_Backend *oldbackend_;
public:
My_Log_Msg_Backend() : oldbackend_(0) ...{}
~My_Log_Msg_Backend()
...{
ACE_OS::printf(ACE_TEXT("My_Log_Msg_Backend::~My_Log_Msg_Backend() "));
// !!!注意销毁后清理ACE_Log_Msg相应状态,ACE_Log_Msg作为
// Singleton形式存在,系统退出时ACE_Object_Manager清理它。
// 这里假设My_Log_Msg_Backend实例生命周期短于ACE_Log_Msg实例,
// 并非所有情况都是正确的
ACE_LOG_MSG->clr_flags(ACE_Log_Msg::CUSTOM);
ACE_LOG_MSG->msg_backend(oldbackend_);
}
void oldbackend(const ACE_Log_Msg_Backend *backend)
...{
oldbackend_ = const_cast<ACE_Log_Msg_Backend *>(backend);
}
virtual int open (const ACE_TCHAR *logger_key)
...{
ACE_OS::printf("My_Log_Msg_Backend::open() logger_key[%s] ", logger_key);
return 0;
}
virtual int reset (void)
...{
return 0;
}
virtual int close (void)
...{
return 0;
}
virtual int log (ACE_Log_Record &log_record)
...{
ACE_OS::printf("[Log_Msg_Backend]%s", log_record.msg_data());
return 0;
}
}
;
//
*******************************************************************************
void
foo()
...
{
ACE_TRACE(ACE_TEXT("foo()"));
ACE_DEBUG((LM_DEBUG, ACE_TEXT("foo() ")));
}
//
*******************************************************************************
int
ACE_TMAIN(
int
argc, ACE_TCHAR
*
argv[])
...
{
std::ofstream os((std::string(argv[0]) + ".log").c_str());
ACE_LOG_MSG->msg_ostream(&os, 0);
My_Log_Msg_Callback lmc;
ACE_Log_Msg_Callback *oldcallback = ACE_LOG_MSG->msg_callback(&lmc);
lmc.oldcallback(oldcallback);
My_Log_Msg_Backend lmb; /**//// ACE_Log_Msg::CUSTOM
ACE_Log_Msg_Backend *oldbackend = ACE_LOG_MSG->msg_backend(&lmb);
lmb.oldbackend(oldbackend);
ACE_LOG_MSG->open(prog_name,
ACE_Log_Msg::STDERR |
ACE_Log_Msg::OSTREAM |
ACE_Log_Msg::MSG_CALLBACK |
ACE_Log_Msg::CUSTOM,
logger_key);
ACE_TRACE(ACE_TEXT("main()"));
ACE_DEBUG((LM_DEBUG, ACE_TEXT("main() ")));
foo();
return 0;
}