ACE日志系统

ACE可以将输出重新定向到stderr(缺省)、系统日志、输出流(Output Stream)、
甚至是回调函数在C/S结构中,ACE也可以实现客户端输出定向到服务器。本节将详细讨论这些内容。


 

三 ACE的重新定向
设置或改变ACE日志输出目标(logging sink)可以通过ACE_Log_Msg的方法open来完成,
另外可通过set_flags和clr_flags来配合实现输出到多个目标。
open方法的原型:
  /**
   * Initialize the ACE logging facility. Supplies the program name
   * that is available to each logging message call. Default arguments
   * set up logging to STDERR only.
   *
   * @param prog_name      The name of the calling program.
   * @param options_flags  A bitwise-or of options flags used to set
   *                       the initial behavior and logging sink(s).
   *                       (see the enum above for the valid values).
   * @param logger_key     The name of ACE_FIFO rendezvous point where
   *                       the local client logger daemon is listening
   *                       for logging messages. Only meaningful if the
   *                       LOGGER bit is set in the @a flags argument.
   */
  int open (const ACE_TCHAR *prog_name,
            u_long options_flags = ACE_Log_Msg::STDERR,
            const ACE_TCHAR *logger_key = 0);
           
第一个参数prog_name表示程序名称,可以任意设置,可通过ACE_Log_Msg的program_name
方法获取,另外当第二个参数设置中有ACE_Log_Msg::VERBOSE时,会在输出信息前加上前缀,
前缀中就包含该program_name。
第二个参数可以是:
STDERR        Write messages to STDERR
LOGGER        Write messages to the local client logger daemon
OSTREAM       Write messages to the assigned output stream
MSG_CALLBACK  Write messages to the callback object
VERBOSE       Prepends program name, timestamp, host name, process ID,
              and message priority to each message
VERBOSE_LITE  Prepends timestamp and message priority to each message
SILENT        Do not print messages at all
SYSLOG        Write messages to the system's event log
CUSTOM        Write messages to the user-provided back end
第三个参数logger key是针对第二个参数为LOGGER(C/S结构)时需要设置的,
表示客户端程序端口值,比如:ACE_DEFAULT_LOGGER_KEY。
一旦调用了该方法,其后的输出将重新定位到第二个参数所指定的sink上。

set_flags、clr_flags的原型:
// Enable the bits in the logger's options flags.
void set_flags (unsigned long f);
// Disable the bits in the logger's options flags.
void clr_flags (unsigned long f);
参数含义同open函数的第二个参数options_flags,其中set_flags调用可在现有输出目标基础上
增加新的输出目标(叠加),clr_flags则从中删除指定的输出目标。
下面描述一下重定向到不同sink的例子。

1.输出到stderr
这个简单:
ACE_LOG_MSG->open (argv[0], ACE_Log_Msg::STDERR);
ACE_DEBUG ((LM_DEBUG, ACE_TEXT ("test/n")));
或者:
ACE_LOG_MSG->set_flags (ACE_Log_Msg::STDERR);
ACE_DEBUG ((LM_DEBUG, ACE_TEXT ("test/n")));
2.输出到System Logger
ACE_LOG_MSG->open(argv[0], ACE_Log_Msg::SYSLOG, ACE_TEXT ("ACE log"));
ACE_DEBUG ((LM_DEBUG, ACE_TEXT ("test/n")));
注意重新定向到System Logger不能用set_flags、clr_flags来实现。Windows操作系统下,
会输出到系统的Event Log中,其中Event Source名称为Open函数第三个参数。Unix/Linux
下会输出到syslog facility。另外Windows下LM_STARTUP、LM_SHUTDOWN、LM_TRACE、
LM_DEBUG、LM_INFO 会对应EVENTLOG_INFORMATION_TYPE;LM_NOTICE、LM_WARNING
会对应EVENTLOG_WARNING_TYPE、LM_ERROR、LM_CRITICAL、LM_ALERT、LM_EMERGENCY
会对应EVENTLOG_ERROR_TYPE。
3.输出到Output Streams
下例将日志输出到文件(ofstream):
#include < ace/Log_Msg.h >
#include < ace/streams.h >
int ACE_TMAIN (int, ACE_TCHAR *argv[])
{
    ACE_OSTREAM_TYPE *output = new ofstream ("test.txt");
    ACE_LOG_MSG->msg_ostream (output, 1);
    ACE_LOG_MSG->set_flags (ACE_Log_Msg::OSTREAM);
    ACE_LOG_MSG->clr_flags (ACE_Log_Msg::STDERR);
    ACE_DEBUG ((LM_DEBUG, ACE_TEXT ("test/n")));
    return 0;
}
上例中首先需要通过msg_ostream来assign一个ACE OSTREAM,然后再指定log sink为
ACE_Log_Msg::OSTREAM,指定log sink既可用set_flags也可用open方法,注意上例中
set_flags之后紧接着又用clr_flags屏蔽掉先前
指定的ACE_Log_Msg::STDERR(缺省sink),这样就不会同时输出到stderr了。
看一下msg_ostream的注释(解释的很明白,不赘述):
/**
   * delete_stream == 1, forces Log_Msg.h to delete the stream in
   * its own ~dtor (assumes control of the stream)
   * use only with proper ostream (eg: fstream), not (cout, cerr)
   */
  void msg_ostream (ACE_OSTREAM_TYPE *, int delete_ostream);
 
上例中由于设置了delete_stream == 1,所以不应再显示地调用delete output;
来删除ofstream。全局性的ostream还包括cout、cerr、clog等,
这里再举一个输出到cout的例子,
其它情况类似:
    ACE_LOG_MSG->msg_ostream (&cout);
    ACE_LOG_MSG->open (argv[0], ACE_Log_Msg::OSTREAM);
    ACE_DEBUG ((LM_DEBUG, ACE_TEXT ("test/n")));
4.输出到Callback
在ACE中,将日志输出重新定向到回调函数其实很简单,只需要四步:
(1)定义ACE_Log_Msg_Callback的派生类,
    实现纯虚函数virtual void log (ACE_Log_Record &log_record).
(2)在ACE_Log_Msg中注册该派生类(比如 MyCallback):
    Callback *callback = new Callback;
    ACE_LOG_MSG->set_flags (ACE_Log_Msg::MSG_CALLBACK);
    ACE_LOG_MSG->clr_flags (ACE_Log_Msg::STDERR);
    ACE_LOG_MSG->msg_callback (callback);
   
(3)调用日志输出宏,比如ACE_DEBUG,就会将输出重新定向到MyCallback的log方法中。
(4)退出前的清理工作:
    ACE_LOG_MSG->clr_flags (ACE_Log_Msg::MSG_CALLBACK);
    delete callback;
注意到ACE_Log_Msg_Callback的log方法的参数类型ACE_Log_Record, 
可以利用其print方法重新定制输出:
int print (const ACE_TCHAR host_name[], u_long verbose_flag, FILE *fp);
或者:
  int print (const ACE_TCHAR host_name[], u_long verbose_flag,
                                            ACE_OSTREAM_TYPE &stream);
   
第一个参数表示主机名,第二个参数可选ACE_Log_Msg::VERBOSE、VERBOSE_LITE
或0(原始信息),第三个参数可以是文件指针或者是标准输出流,比如:
    log_record.print (ACE_TEXT (""), ACE_Log_Msg::VERBOSE, cout);      
 /* 输出到屏幕 */
    log_record.print (ACE_TEXT (""), 0, ofstream("test.txt",ios::app));
/* 输出到文件 */
关于ACE_Log_Record进一步使用,请参考以下例子:
/* main.cpp */
#include < ace/Task.h >
#include < ace/Log_Msg.h >
#include " MyCallback.h "
int ACE_TMAIN (int, ACE_TCHAR *[])
{
    MyCallback *callback = new MyCallback;
    ACE_LOG_MSG->set_flags (ACE_Log_Msg::MSG_CALLBACK);
    ACE_LOG_MSG->clr_flags (ACE_Log_Msg::STDERR);
    ACE_LOG_MSG->msg_callback (callback);
    ACE_DEBUG ((LM_DEBUG, ACE_TEXT ("test1/n")));
    ACE_DEBUG ((LM_INFO, ACE_TEXT ("test2/n")));
    ACE_LOG_MSG->clr_flags (ACE_Log_Msg::MSG_CALLBACK);
    delete callback;
    return 0;
}

/* MyCallback.h */
#include < ace/streams.h >
#include < ace/Log_Msg.h >
#include < ace/Log_Msg_Callback.h >
#include < ace/Log_Record.h >
#include < ace/streams.h >
#include < ace/Log_Msg_Callback.h >
#include < ace/Log_Record.h >
#include < ace/SString.h >
#include < ace/OS.h >
class MyCallback : public ACE_Log_Msg_Callback
{
public:
    void log (ACE_Log_Record &log_record)
    {
        cerr << "Log Message Received:" << endl;
        unsigned long msg_severity = log_record.type ();
    
ACE_Log_Priority prio = ACE_static_cast (ACE_Log_Priority, msg_severity);
    const ACE_TCHAR *prio_name = ACE_Log_Record::priority_name (prio);
        cerr << "/tType:        "
             << ACE_TEXT_ALWAYS_CHAR (prio_name)
             << endl;
        cerr << "/tLength:      " << log_record.length () << endl;
        const time_t epoch = log_record.time_stamp ().sec ();
        cerr << "/tTime_Stamp:  "
             << ACE_TEXT_ALWAYS_CHAR (ACE_OS::ctime (&epoch))
             << flush;
        cerr << "/tPid:         " << log_record.pid () << endl;
        ACE_CString data (">> ");
        data += ACE_TEXT_ALWAYS_CHAR (log_record.msg_data ());
        cerr << "/tMsgData:     " << data.c_str () << endl;
    }
};
输出:
Log Message Received:
        Type:        LM_DEBUG
        Length:      32
        Time_Stamp:  Mon Mar 27 17:03:06 2005
        Pid:         2752
        MsgData:     >> test1
Log Message Received:
        Type:        LM_INFO
        Length:      32
        Time_Stamp:  Mon Mar 27 17:03:06 2005
        Pid:         2752
        MsgData:     >> test2
       
需要提醒的是,由于ACE_Log_Msg是线程相关的,而且spawn出来的线程没有继承性,
因此Callback仅对单个线程有效,如果想实现多个线程的callback,
需要分别在各个线程中调用:ACE_LOG_MSG->msg_callback (callback);
5.C/S结构中输出到服务器
可用于分布式系统的日志处理(distributed logger),logging server运行在
某台主机,并接收来自其它主机的logging requests. 在其它主机(也可能在本机)
需要运行一个logging client daemon进程,相当于一个proxy,
实现了你自己编写的程序与logging server的交互。自己编写的程序格式如下:
#include < ace/Log_Msg.h >
 
 
int ACE_TMAIN (int, ACE_TCHAR *argv[])
{
  ACE_LOG_MSG->open (argv[0],
                     ACE_Log_Msg::LOGGER,
                     ACE_DEFAULT_LOGGER_KEY);
  ACE_DEBUG ((LM_DEBUG, ACE_TEXT ("Sent to Logging Server/n")));
  return 0;
}
在运行你自己编写的程序之前需要:
(1)首先运行logging server:
%ACE_ROOT%/netsvcs/servers/main -f server.conf
(2)然后运行logging client daemon:
%ACE_ROOT%/netsvcs/servers/main -f client.conf
其中server.conf内容如下:
dynamic Logger Service_Object * ACE:_make_ACE_Logging_Strategy() "-s log.txt -f STDERR|OSTREAM"
dynamic Server_Logging_Service Service_Object * netsvcs:_make_ACE_Server_Logging_Acceptor()
active "-p 20009"
该文件每行以dynamic开始,第一行定义了logging strategy,
将会把日志输出到log.txt(-s 参数),同时输出到STDERR。
第二行定义了logging server侦听(listen)端口为20009。
client.conf内容如下:
dynamic Client_Logging_Service Service_Object * netsvcs:_make_ACE_Client_Logging_Acceptor()
active "-p 20009 -h localhost"
该文件同样以dynamic开始,定义了logging client daemon的侦听端口为2009
(与server.conf一致),同时-h指定目标机器名称或ip(这里是本机)。
运行了logging server和logging client daemon之后,编译运行上面那个程序, 
logging server会显示:
Sent to Logging Server
同时在logging server所在目录(%ACE_ROOT%/netsvcs/servers/)下
会生成log.txt,内容是:
starting up Logging Server at port 20009 on handle 1900
Sent to Logging Server
注意到之所以如此麻烦地启动两级logging管理,主要是考虑到分布式处理过程中
可能出现的瓶颈问题。当然也可以直接通过ACE_SOCK_Stream实现与l
ogging server的交互,而不用logging client daemon,细节以后讨论。
而利用server.conf和client.conf来配置分布式logging服务涉及到了ACE的
Runtime Configuration,细节同样在以后会讨论到。对其中参数的解释列举如下:
-f Specify ACE_Log_Msg flags (OSTREAM, STDERR, LOGGER, VERBOSE, 
   SILENT, VERBOSE_LITE) used to control logging.
-i The interval, in seconds, at which the log file size is sampled
   (default is 0; do not sample by default).
-k Specify the rendezvous point for the client logger
    (比如ACE_DEFAULT_LOGGER_KEY)
-m The maximum log file size in Kbytes.
-n Set the program name for the %n format specifier.
-N The maximum number of log files to create.
-o Request the standard log file ordering (keeps multiple log files
  in numbered order). Default is not to order log files.
-p Pass in the processwide priorities to either enable (DEBUG, INFO,
  WARNING,NOTICE, ERROR,CRITICAL, ALERT, EMERGENCY) or to disable 
(~DEBUG, ~INFO, ~WARNING, ~NOTICE, ~ERROR, ~CRITICAL, ~ALERT, ~EMERGENCY).
-s Specify the file name used when OSTREAM is specified as an output target.
-t Pass in the per instance priorities to either enable (DEBUG, INFO,
WARNING, NOTICE, ERROR, CRITICAL, ALERT, EMERGENCY) or to disable 
(~DEBUG, ~INFO, ~WARNING, ~NOTICE, ~ERROR, ~CRITICAL, ~ALERT, ~EMERGENCY).
-w Cause the log file to be wiped out on both start-up and reconfiguration.
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值