ACE日志系统

2023-11-11

一、简介

以前曾介绍过一个开源日志系统log4cplus,ACE也有自己的Logging Facility,与log4cplus相似,ACE日志系统也具有线程安全、灵活、分级显示等特点,可以面向程序调试、运行、测试、和维护等全生命周期,可以选择将信息输出到屏幕、文件、系统日志(如Windows下的Event log)、甚至是远程服务器。除此之外,ACE日志系统支持回调函数以及运行时刻动态配置(Runtime Configuration ),本文主要参考了《ACE Programmer's Guide,The: Practical Design Patterns for Network and Systems Programming》。

二、ACE日志系统的基本使用

首先通过一个引例介绍如何调用ACE Log Macro:

#include < ace/Log_Msg.h >

int ACE_TMAIN (int, ACE_TCHAR *[])

{

ACE_DEBUG ((LM_INFO, ACE_TEXT ("Hi ACE Logging Facility!\n")));

return 0;

}

程序需要包含头文件:Log_Msg.h,该文件定义了各种有用的日志输出宏,宏ACE_TMAIN 是ACE定义的wide-character-enabled entry point ,ACE日志系统缺省输出到stderr,所以对于Console类型的应用程序,会直接输出到屏幕。运行该程序时屏幕上会显示:

Hi ACE Logging Facility!

可以看出ACE日志系统的宏调用非常简单:

ACE_XXX((severity, formatting-args));

其中,ACE_XXX表示日志输出宏(ACE Logging Macros),包括

ACE_ERROR((level, string, ...))

ACE_DEBUG((level, string, ...))

ACE_TRACE(string)

ACE_ASSERT(test)

ACE_HEX_DUMP((level, buffer, size [,text]))

ACE_RETURN(value)

ACE_ERROR_RETURN((level, string, ...), value)

ACE_ERROR_INIT( value, flags )

ACE_ERROR_BREAK((level, string, ...))

severity即输出的严重等级(Severity Level),包括:


       LM_SHUTDOWN  Shutdown the logger (decimal 1)

LM_STARTUP  Initialize the logger (decimal 64).

LM_TRACE Messages indicating function-calling sequence

LM_DEBUG Debugging information

LM_INFO Messages that contain information normally of use only when debugging a program

LM_NOTICE Conditions that are not error conditions but that may require special handling

LM_WARNING Warning messages

LM_ERROR Error messages

LM_CRITICAL Critical conditions, such as hard device errors

LM_ALERT A condition that should be corrected immediately, such as a corrupted database

LM_EMERGENCY A panic condition, normally broadcast to all usersLM_TRACE Messages indicating function-calling sequence

formatting-args是要输出的内容,格式类似于printf函数的输出格式:

Code Argument Type Displays

A ACE_timer_t 浮点数

a — 导致程序终止(Abort)

c char 单个字符

C char* 字符串(narrow characters)

i,d int 10进制整数

I — 缩进

e,E,f,F,g,G double 双精度浮点数

l — 行号

M — severity level的名称

m — 错误号(errorno)

N — 文件名

n — ACE_Log_Msg::open()指定的程序名

o int 八进制整数

P — 当前进程ID

p ACE_TCHAR* 字符串,后接错误号,同perror

Q ACE_UINT64 64位无符号十进制整数

r void (*)() 函数调用

R int 十进制整数

S int 数字对应的信号名称

s ACE_TCHAR* ACE_TCHAR类型的字符串

T — 当前时间(hour:minute:sec.usec)

D — 时戳(month/day/year/hour:minute:sec.usec)

t — 当前线程ID

u int 无符号十进制整数

w wchar_t Single wide character

W wchar_t* Wide-character string

x,X int 十六进制数

@ void* 指针(十六进制)

% N/A %

我们可以通过一些例子来进一步了解如何使用这些宏、Severity Level和格式,在开始之前,首先澄清一个概念:

一些日志系统(如log4cplus)是靠宏名来区分日志信息类型的,比如:

LOG4CPLUS_DEBUG(Logger::getRoot(),"some text") /* DEDUG 类型 */

LOG4CPLUS_ERROR(Logger::getRoot(),"some text") /* ERROR 类型 */

ACE的日志系统不是靠宏名本身,而是靠宏的第一个参数,即Severity Level来区分日志信息类型:

ACE_DEBUG((LM_DEBUG, ACE_TEXT ("some text\n"))); /* DEDUG 类型 */

ACE_ERROR((LM_DEBUG, ACE_TEXT ("some text\n"))); /* DEDUG 类型 */

ACE_DEBUG((LM_ERROR, ACE_TEXT ("some text\n"))); /* ERROR 类型 */

ACE_ERROR((LM_ERROR, ACE_TEXT ("some text\n"))); /* ERROR 类型 */

从这个角度考虑,宏ACE_DEBUG和ACE_ERROR没有什么区别,几乎可以混用,这两个宏的区别主要体现在两方面:

a) 语义上的差别

b) 语句执行完后,op_status不同,比如:

ACE_Log_Msg *lm = ACE_LOG_MSG;

ACE_DEBUG ((LM_ERROR, ACE_TEXT ("some text\n")));

ACE_ASSERT( 0 == lm->op_status() ); /* ACE_DEBUG执行完后,op_status 是0 */

ACE_ERROR ((LM_ERROR, ACE_TEXT ("some text\n")));

ACE_ASSERT( -1 == lm->op_status() ); /* ACE_ERROR执行完后,op_status 是-1 */

下面举一些典型用例:

【use-case1】 如何在编译时刻Enabling 或Disabling Logging Macros

在#include < ace/Log_Msg.h >之前定义一些宏,可以在编译时刻Enable/Disable logging Macros

/* disable ACE_DEBUG and ACE_ERROR */

#define ACE_NLOGGING

/* enable ACE_DEBUG and ACE_ERROR(缺省) */

#undef ACE_NLOGGING

/* enable ACE_ASSERT(缺省)同时Windows的NDUBUG宏未定义未启用*/

#undef ACE_NDEBUG

/* disable ACE_ASSERT */

#define ACE_NDEBUG

/* enable ACE_TRACE */

#define ACE_NTRACE 0

/* disable ACE_TRACE(缺省) */

#define ACE_NTRACE 1

【use-case2】格式使用

a) 打印完结果后终止(Abort)

ACE_DEBUG ((LM_DEBUG, ACE_TEXT ("begin %a end \n")));

输出以下信息后提示Abort信息:

begin Aborting... end

b) 缩进

int ACE_TMAIN (int, ACE_TCHAR *[])

{

ACE_TRACE("main");

ACE_DEBUG ((LM_DEBUG, ACE_TEXT ("%Itext\n")));

return 0;

}

输出:

(700) calling main in file `D:\aceprj\testlog\main.cpp' on line 24

text

(700) leaving main

(其中,700是ACE_TRACE控制输出的当前线程ID)

c) HEX Dump(调试时候比较有用)

char szBuf[128] = "hello world!";

ACE_HEX_DUMP((LM_DEBUG, szBuf, 128,"szBuf: "));

输出:

szBuf: - HEXDUMP 128 bytes

68 65 6c 6c 6f 20 77 6f 72 6c 64 21 00 00 00 00 hello world!....

00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................

00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................

00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................

00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................

00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................

00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................

00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................

d)函数调用

void foo(void)

{

ACE_DEBUG ((LM_DEBUG, ACE_TEXT ("in func foo\n")));

}

int ACE_TMAIN (int, ACE_TCHAR *[])

{

ACE_DEBUG ((LM_DEBUG, ACE_TEXT ("call function %r\n"),foo));

return 0;

}

输出:

in func foo

call function

e) 进制转换

ACE_UINT64 uint64A = 210113198510308319;

int hexA = 0xBEEE3F;

int octA = 9;

ACE_timer_t tmInterval = 0.000001;

ACE_DEBUG ((LM_DEBUG, ACE_TEXT ("UINT64: %Q\n"),uint64A));

ACE_DEBUG ((LM_DEBUG, ACE_TEXT ("HEX: %X, hex: %x\n"),hexA,hexA));

ACE_DEBUG ((LM_DEBUG, ACE_TEXT ("Ptr in hex:%@\n"),&hexA));

ACE_DEBUG ((LM_DEBUG, ACE_TEXT ("Octal number of 9:%o\n"),octA));

ACE_DEBUG ((LM_DEBUG, ACE_TEXT ("timer's Interval: %A\n"),tmInterval));

输出:

UINT64: 210113198510308319

HEX: BEEE3F, hex: beee3f

Ptr in hex:0012FE30

Octal number of 9:11

timer's Interval: 0.000001

f)综合使用

ACE_DEBUG ((LM_DEBUG, ACE_TEXT ("%D, %M, [%t], Line:%l\n")));

ACE_DEBUG ((LM_ERROR, ACE_TEXT ("%D, %M, [%t], Line:%l\n")));

输出:

Mon Mar 20 2005 20:02:16.912000, LM_DEBUG, [3296], Line:24

Mon Mar 20 2006 20:02:16.912000, LM_ERROR, [3296], Line:25

【use-case3】ACE_RETURN宏使用

a) ACE_RETURN

int foo(void)

{

ACE_RETURN(3);

}

int ACE_TMAIN (int, ACE_TCHAR *[])

{

foo();

ACE_ASSERT( 3 == ACE_LOG_MSG->op_status() );

return 0;

}

输出:(无任何输出)

b) ACE_ERROR_RETURN

int foo(void)

{

ACE_ERROR_RETURN((LM_DEBUG,"Return error\n"), 3);

}

int ACE_TMAIN (int, ACE_TCHAR *[])

{

foo();

ACE_ASSERT( 3 == ACE_LOG_MSG->op_status() );

return 0;

}

输出:

Return error

c) ACE_ERROR_BREAK

int i = 0;

while(1)

{

if( i > 10 )

{

ACE_ERROR_BREAK((LM_DEBUG,"break from while\n"));

}

i++;

}

ACE_DEBUG((LM_DEBUG, "i = %d\n", i));

输出:

break from while

i = 11

【use-case4】 如何在运行时刻Enabling 或Disabling Logging Severities

由于缺省情况下在进程范围内所有logging severities都会被输出,因此需要通过priority_mask、ACE_Log_Msg::enable_debug_messages、ACE_Log_Msg::disable_debug_messages来过滤输出。

其中priority_mask的用法如下:

/* Get the current ACE_Log_Priority mask. */

u_long priority_mask (MASK_TYPE = THREAD);

/* Set the ACE_Log_Priority mask, returns original mask. */

u_long priority_mask (u_long, MASK_TYPE = THREAD);

这里MASK_TYPE可选择:ACE_Log_Msg::PROCESS(作用范围是进程)或ACE_Log_Msg::THREAD(作用范围是线程)

enable_debug_messages/disable_debug_messages的用法如下:

static void disable_debug_messages(ACE_Log_Priority priority = LM_DEBUG);

static void enable_debug_messages(ACE_Log_Priority priority = LM_DEBUG);

这里输入参数可选择任意需要打开或屏蔽的logging severity,不像函数名称暗示的那样只能控制LM_DEBUG类型,另外由于是静态函数,enable_debug_messages/disable_debug_messages的设置对整个进程有效。

注意一下priority_mask与enable_debug_messages/disable_debug_messages的区别:

priority_mask将进程(或线程)的logging severitys用其第一个参数所代替,清除之前的设置。

enable_debug_messages将进程的logging severitys用其参数去累加,不清除之前的设置。

disable_debug_messages将进程的logging severitys用其参数去屏蔽,不清除之前的设置。

a) 整个进程仅允许输出LM_ERROR和LM_WARNING

ACE_DEBUG((LM_DEBUG, "Debug Text1\n"));

ACE_DEBUG((LM_ERROR, "Error Text1\n"));

ACE_LOG_MSG->priority_mask (LM_ERROR | LM_WARNING, ACE_Log_Msg::PROCESS);

ACE_DEBUG((LM_DEBUG, "Debug Text2\n"));

ACE_DEBUG((LM_ERROR, "Error Text2\n"));

或者:

ACE_DEBUG((LM_DEBUG, "Debug Text1\n"));

ACE_DEBUG((LM_ERROR, "Error Text1\n"));

/* 禁用了进程所有的logging severitys */

ACE_LOG_MSG->priority_mask (0, ACE_Log_Msg::PROCESS);

ACE_Log_Msg::enable_debug_messages (LM_ERROR);

ACE_Log_Msg::enable_debug_messages (LM_WARNING);

ACE_DEBUG((LM_DEBUG, "Debug Text2\n"));

ACE_DEBUG((LM_ERROR, "Error Text2\n"));

输出:

Debug Text1

Error Text1

Error Text2

b) 每个线程控制各自的logging severity

#include < ace/Task.h > /* for ACE_Thread_Manager */

#include < ace/Log_Msg.h >

void service(void)

{

ACE_LOG_MSG->priority_mask (LM_INFO, ACE_Log_Msg::THREAD);

ACE_DEBUG((LM_DEBUG, "in service, MsgType:%M, ThreadID: %t\n"));

ACE_DEBUG((LM_INFO, "in service, MsgType:%M, ThreadID: %t\n"));

}

void worker(void)

{

ACE_LOG_MSG->priority_mask (LM_DEBUG, ACE_Log_Msg::THREAD);

ACE_DEBUG((LM_DEBUG, "in worker, MsgType:%M, ThreadID: %t\n"));

ACE_DEBUG((LM_INFO, "in worker, MsgType:%M, ThreadID: %t\n"));

}

int ACE_TMAIN (int, ACE_TCHAR *[])

{

ACE_LOG_MSG->priority_mask (0, ACE_Log_Msg::PROCESS);

ACE_Thread_Manager::instance ()->spawn ((ACE_THR_FUNC)service);

ACE_Thread_Manager::instance ()->spawn_n (3, (ACE_THR_FUNC)worker);

ACE_Thread_Manager::instance ()->wait();

return 0;

}

输出:

in service, MsgType:LM_INFO, ThreadID: 3428

in worker, MsgType:LM_DEBUG, ThreadID: 2064

in worker, MsgType:LM_DEBUG, ThreadID: 1240

in worker, MsgType:LM_DEBUG, ThreadID: 1800

三、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 LoggerACE_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: >> test1Log 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实现与logging 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.



本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

ACE日志系统 的相关文章

  • 更改 TFS 构建代理的身份

    我们有一个运行集成测试的 TFS 构建代理 其中一些测试会调用 Internet 上的 Web 服务 我们的网络使用代理与互联网通信 由于构建代理在网络服务上的身份下运行 并且网络服务的设置未配置为使用代理 因此测试失败 我该如何执行以下操
  • 哪里可以找到蝗虫原木?

    我正在使用 Locust 对我们的应用程序进行压力测试 我收到错误 因为 POST 调用似乎不正确 在哪里可以看到蝗虫的日志 我想看看邮寄电话是什么样子 看看出了什么问题 这是我的代码 以防有人可以告诉我我做错了什么 from locust
  • 匿名监听器与弱引用不兼容吗?

    我正在阅读刚刚被问到的这个问题 避免回调中的内存泄漏 https stackoverflow com questions 8475314 我很困惑 直到有人回答了以下问题 这种方法的问题是你不能拥有一个仅在集合中引用的侦听器 因为它会随机消
  • 如何获取Android崩溃日志?

    我有一个不在市场上的应用程序 使用调试证书签名 但希望在我的应用程序崩溃时获取崩溃日志数据 在哪里可以找到我的应用程序崩溃原因的日志 如果您的应用程序正在被其他人下载并在远程设备上崩溃 您可能需要查看 Android 错误报告库 参考这个帖
  • WCF 回调通道被过早处置?

    我的应用程序正在使用带有回调通道的 net tcp WCF 服务 由于某种原因 我无法发送事件回调 这就是我正在做的事情 所有代码都在服务器端 初始化时 OperationContext Context get protected set
  • 如何设置 commons-logging 来使用 logback?

    我们使用 slf4j logback 并且碰巧有一些使用 commons logging 的第三方库 如何设置它以使用 logback 答案是不要使用 commons logging jar 因为 SLF4J 的设计目的与 commons
  • 如何在 Node.js 中使用 Winston 将日志存储到 mysql 数据库

    我正在使用 winston 为我的应用程序进行日志记录 我已经使用这个完成了文件传输 class LoggerHelper extends BaseHelper constructor cApp super cApp this props
  • 停止或终止服务

    我正在开发一个任务管理器应用程序 在该应用程序中 我向用户显示正在运行的应用程序列表以及每个应用程序旁边的终止按钮 当用户按下终止按钮时 相应的活动将被终止 我使用了 activitymanager killbackgroundproces
  • 如何显示 RSpec 测试生成的 SQL 查询日志?

    我正在为我的 Rails 3 应用程序编写规范 我想测试数据库事务是否真的有效 如果能够看到我的应用程序在规范驱动下生成的 sql 查询 这将非常有帮助 有没有办法像在 Rails 控制台中一样查看查询 我正在使用 Rails 3 0 9
  • 我可以使用 lambda 函数或 std::function 对象来代替函数指针吗?

    我有一个需要使用的库 它定义了以下内容 typedef void CallbackFunction const int i 并且有一个注册回调的函数 如下所示 void registerCallback CallbackFunction p
  • .htaccess 主要用于 localhost

    我在本地主机上使用 wamp server 虚拟主机名为www xyz com我有一个通过 git 设置的部署服务 它将代码部署到服务器上www xyz in 这是我的 htaccess 代码 RewriteEngine on Rewrit
  • android 销毁时是否有任何视图回调?

    我有一个自定义视图组件 我在片段或活动中使用了它 我想知道当它从片段 活动中销毁时是否有回调 View 没有回调 除了finalize 但我不认为这就是你所要求的 查看有onDetachedFromWindow 当它从屏幕上移除时 但这与它
  • 如何登录Oracle数据库?

    我对 Oracle 数据库中常用的日志记录方法感兴趣 我们的方法如下 我们为要记录的表创建一个日志表 日志表包含原始表的所有列以及一些特殊字段 包括时间戳 修改类型 插入 更新 删除 修改者的 id 原始表上的触发器为每次插入和删除创建一个
  • 使用可执行 JAR 时指定 Log4j2 配置文件

    使用可执行 JAR 文件时 我在指定 Log4j2 配置文件位置时遇到问题 如果我将所有 JAR 分开 效果很好 但是当我尝试将它们组合成一个可执行 JAR 文件时 由于某种原因log4j2 xml未从命令行获取文件 我尝试过这两种指定位置
  • PHP 和 ADODB 连接失败

    我的任务是迁移服务器 这包括移动我没有构建的应用程序 其中一些具有 ADODB connection 我似乎无法在新服务器上修复它 我只得到空白屏幕 我已经对 ADODB connection 与 PHP 进行了相当广泛的研究 但找不到明确
  • 快速将文本附加到文本框

    我有一个BackgroundWorker正在发布消息的线程 使用BeginInvoke在 GUI 中的文本框中 方法 write debug text 在文本框中显示文本使用AppendText并将文本写入Console 外观上是这样的Ba
  • Android 在启动时启动服务,如何在设备重启后重新启动服务类?

    我需要在启动时启动一项服务 我搜索了很多 他们正在谈论广播接收器 由于我是 Android 开发新手 所以我对 Android 上的服务并没有清楚的了解 请提供一些源代码 您的接收者 public class MyReceiver exte
  • 不调用 PreviewCallback 和带缓冲区的 PreviewCallback

    我对 Android 4 0 x 的预览回调有疑问 我设置了一个相机 创 建一个表面来显示相机图像on previewCallback 事件 一切正常 但对于 Android 4 0 x 则不然onPreviewCallback被称为 也不
  • 保存日志 - SimpleHTTPServer

    如何保存控制台的输出 例如 192 168 1 1 2014 年 8 月 18 日 12 05 59 代码 404 消息未找到文件 到一个文件 这是代码 import SimpleHTTPServer import SocketServer
  • 如何与使用 child_process.spawn 创建的新创建的服务器交互

    我正在尝试为我的私人托管的 反恐精英全球攻势 服务器制作一个前端 当我点击运行服务器时 在前端 一切正常 服务器启动并记录到控制台 但是如何查看服务器IP地址 服务器中的玩家等信息呢 这是我到目前为止运行服务器的内容 router post

随机推荐

  • PTA(浙大版《C语言程序设计(第3版)》题目集)习题6-4 使用函数输出指定范围内的Fibonacci数 (20 分)

    PTA 浙大版 C语言程序设计 第3版 题目集 习题6 4 使用函数输出指定范围内的Fibonacci数 20 分 本题要求实现一个计算Fibonacci数的简单函数 并利用其实现另一个函数 输出两正整数m和n 0
  • [科普] 狭义相对论

    本文转载至 http www dlkp gov cn keputiandi universe article dispArticle Asp ID 58 第一章 狭义相对论 一 狭义相对论思想的根源 1 法拉第 麦克斯维将电磁学推向 场 的
  • 支付宝小程序框架分析

    支付宝小程序框架逆向分析 本文对支付宝小程序的正向开发做了简单介绍 并从正向开发的文件类型入手 对小程序的宿主框架进行了逆向分析 包括运行机制 通信模型以及安全防护体系等内容 代码开发 支付宝小程序开发在语法方面与传统的前端网页开发非常类似
  • Python——发送邮件

    一 smtplib模块 主要通过SMTP类与邮件系统进行交互 使用方法如下 1 实例化一个SMTP对象 s smtplib SMTP 邮件服务地址 端口号 s smtplib SMTP SSL 邮件服务地址 端口号 2 登陆邮件 权限验证
  • Linux:全志H3图像codec使用笔记

    1 前言 限于作者能力水平 本文可能存在谬误 因此而给读者带来的损失 作者不做任何承诺 2 图像 codec 概述 图像编解码器 codec 包含 Encoder 和 Decoder 两部分功能 我们用下列分别说明 Encoder 和 De
  • 解决window平台下cocos creator 构建发布面板打开后无法显示问题

    项目场景 creator构建发布界面 问题描述 window平台下cocos creator 构建发布面板打开后无法显示 原因分析 项目的配置文件目录setting和local目录内容混乱可能会导致该问题 这时候后重装creator也是没用
  • 设计模式全解析(一)——带你搞懂设计模式

    各位朋友 一提到 设计模式 四个字 是不是有一种若隐若现的朦胧感 我知道设计模式 我知道单例 工厂 观察者 策略 但是设计模式到底是什么呢 解决了什么问题呢 接下来我会一步一步解析一下设计模式 以及各个不同的设计模式到底要做的是什么 我们先
  • Octave的一些基本操作和语法,快速上手Octave,用实例解释

    基本语法 1 直接计算 gt gt 1 2 ans 3 2 变量计算 在最后加上分号 可以不输出结果 gt gt a 1 gt gt b 2 gt gt a b ans 3 3 矩阵赋值 gt gt a 1 2 3 4 5 6 a 1 2
  • Java实现五子棋小游戏(附源码)

    一 效果展示 二 游戏介绍 五子棋 是一种两人对弈的纯策略型棋类游戏 棋具与围棋通用 是起源于中国古代的传统黑白棋种之一 发展于日本 流行于欧美 容易上手 老少皆宜 而且趣味横生 引人入胜 不仅能增强思维能力 提高智力 而且富含哲理 有助于
  • [ Linux ] 静态代码检测工具 —— Cppcheck工具

    文章目录 cppcheck工具介绍 Linux安装 linux使用示例 在makefile中添加cppcheck工具实例 cppcheck工具介绍 什么是静态代码检查 静态代码检查是指在不运行程序的条件下 进行程序分析的方法 有些程序分析需
  • Git 分支管理策略汇总

    原文链接 Git 分支管理策略 最近 团队新入职了一些小伙伴 在开发过程中 他们问我 Git 分支是如何管理的 以及应该怎么提交代码 我大概说了一些规则 但仔细想来 好像也并没有形成一个清晰规范的流程 所以查了一些资料 总结出下面这篇文章
  • 企业如何制定实施MES管理系统的预算方案

    随着制造业的不断发展 MES生产管理系统逐渐成为制造企业提高生产效率 优化资源利用和提升质量水平的重要工具 制定实施MES管理系统的预算方案是企业在进行MES选型和实施时必须考虑的问题 本文将介绍制定实施MES管理系统预算方案的关键步骤 包
  • Nginx常用功能(配置静态资源)

    Nginx配置静态资源 背景 现场业务系统域名均由nginx转发 需求 通过nginx设置静态访问页面 操作如下 1 将静态文件存放在 opt nginx html 下 2 修改nginx conf文件 引用此静态文件 server lis
  • python爬虫爬取使用Ajax请求的网站数据解析——以梅老板微博为例(m.weibo.cn)

    文章目录 前言 什么是Ajax Ajax基本原理 发送请求 解析内容 渲染网页 Ajax分析方法 查看请求 过滤请求 Ajax结果提取 1 分析请求 since id解析 2 分析响应 3 爬取微博数据 后记 前言 前面学习了使用正则表达式
  • 客户服务器显示例子,WebSocket实现数据库更新前台实时显示(示例代码)

    通过一个小实例来实现数据库更新后 推送消息给前台 让前台进行相应操作 需求 数据库更新之后服务器推送消息给前台 让前台做操作 数据库的数据不是由服务器写入的 实现的话说到底都是用轮询 因为数据库的数据不是通过后台插入更新的 所以无论用什么办
  • [解决方法]PermissionError: [Errno 13] Permission denied:

    写Python工程代码 花了我5个小时排错 最后发现是写入文件的时候 权限不够 我做个测试 向C盘根目录写入文件 a C 日志 test txt with open a w encoding utf 8 as file object fil
  • VScode+Keil搭建STM32开发环境

    一 下载MinGW VScode 这是一款编译器 Visual Studio Code中不自带编译器 在没有安装visual studio的前提下 如果没有的话会报错 链接 https pan baidu com s 1oOT9wzFcSS
  • 根据经纬度计算距离的公式

    private const double EARTH RADIUS 6378 137 private static double rad double d return d Math PI 180 0 public static doubl
  • 百度AI平台人脸动漫化python测试

    文章目录 获取百度API 获取token 使用 获取百度API 百度转换风格网址 获取API Key AK 和Secret Key SK 获取token import requests import base64 client id 为官网
  • ACE日志系统

    ACE中日志系统 一 简介 以前曾介绍过一个开源日志系统log4cplus ACE也有自己的Logging Facility 与log4cplus相似 ACE日志系统也具有线程安全 灵活 分级显示等特点 可以面向程序调试 运行 测试 和维护