DEBUG系统
level 级别控制 输出target 控制 在终端中更改level 和target:使用环境变量实现 log 不同级别使用不同颜色
# ifndef _DLOG_H_
# define _DLOG_H_
int set_log_level ( char * log_level_str, int enalbe) ;
int set_log_target ( char * log_target, int enable, char * filename) ;
int dlog_debug ( char * file, int line, const char * func, char * format, . . . ) ;
int dlog_info ( char * file, int line, const char * func, char * format, . . . ) ;
int dlog_notice ( char * file, int line, const char * func, char * format, . . . ) ;
int dlog_warn ( char * file, int line, const char * func, char * format, . . . ) ;
int dlog_error ( char * file, int line, const char * func, char * format, . . . ) ;
int dlog_crit ( char * file, int line, const char * func, char * format, . . . ) ;
int dlog_printf ( char * file, int line, const char * func, char * format, . . . ) ;
int dlog_printf_exit ( char * file, int line, const char * func, char * format, . . . ) ;
# define PRINTF_EXIT ( a, . . . ) dlog_printf_exit ( __FILE__ , __LINE__ , __func__ , a, ## __VA_ARGS__)
# define PRINTF ( a, . . . ) dlog_printf ( __FILE__ , __LINE__ , __func__ , a, ## __VA_ARGS__)
# define DEBUG ( a, . . . ) dlog_debug ( __FILE__ , __LINE__ , __func__ , a, ## __VA_ARGS__)
# define INFO ( a, . . . ) dlog_info ( __FILE__ , __LINE__ , __func__ , a, ## __VA_ARGS__)
# define NOTICE ( a, . . . ) dlog_notice ( __FILE__ , __LINE__ , __func__ , a, ## __VA_ARGS__)
# define WARN ( a, . . . ) dlog_warn ( __FILE__ , __LINE__ , __func__ , a, ## __VA_ARGS__)
# define ERROR ( a, . . . ) dlog_error ( __FILE__ , __LINE__ , __func__ , a, ## __VA_ARGS__)
# define CRIT ( a, . . . ) dlog_crit ( __FILE__ , __LINE__ , __func__ , a, ## __VA_ARGS__)
# endif
# define _GNU_SOURCE
# include <stdio.h>
# include <stdlib.h>
# include <string.h>
# include <syslog.h>
# include <stdarg.h>
# include <time.h>
# define DLOG_LOG_LEVEL_ENV "DLOG_LOG_LEVEL"
# define DLOG_LOG_TARGET_ENV "DLOG_LOG_TARGET"
# define DLOG_LOG_TARGET_FILE_ENV "DLOG_LOG_TARGET_FILE"
# define DLOG_LOG_DEFAULT_FILE "/tmp/dlog"
# define ARRARY_SIZE ( a) ( sizeof ( a) / sizeof ( a[ 0 ] ) )
# define MESSAGE_SIZE ( 4096 )
enum {
DLOG_NONE = 0 ,
DLOG_CRIT,
DLOG_ERR,
DLOG_WARN,
DLOG_NOTICE,
DLOG_INFO,
DLOG_DEBUG,
DLOG_PRINTF,
DLOG_TARGET_SYSLOG,
DLOG_TARGET_STDERR,
DLOG_TARGET_FILE,
} ;
struct dlog_context
{
int log_level;
int log_target;
char * file_name;
FILE * file_out;
char message_buf[ MESSAGE_SIZE] ;
} ;
struct level2str
{
int level;
char * level_str;
} ;
int level_to_syspt[ ] = {
[ DLOG_CRIT] = LOG_CRIT,
[ DLOG_ERR] = LOG_ERR,
[ DLOG_WARN] = LOG_WARNING,
[ DLOG_NOTICE] = LOG_NOTICE,
[ DLOG_INFO] = LOG_INFO,
[ DLOG_DEBUG] = LOG_DEBUG,
} ;
struct level2str key_to_str[ ] = {
{ DLOG_NONE, "none" } ,
{ DLOG_CRIT, "crit" } ,
{ DLOG_ERR, "err" } ,
{ DLOG_WARN, "warn" } ,
{ DLOG_NOTICE, "notice" } ,
{ DLOG_INFO, "info" } ,
{ DLOG_DEBUG, "debug" } ,
{ DLOG_PRINTF, "printf" } ,
{ DLOG_TARGET_SYSLOG, "syslog" } ,
{ DLOG_TARGET_STDERR, "stderr" } ,
{ DLOG_TARGET_FILE, "file" } ,
} ;
struct dlog_context logContext = {
. log_level = DLOG_ERR,
. log_target = DLOG_TARGET_STDERR,
. file_name = NULL ,
. file_out = NULL ,
} ;
int set_log_level ( char * log_level_str, int enable)
{
return ( 0 == setenv ( DLOG_LOG_LEVEL_ENV, log_level_str, enable) ) ;
}
int set_log_target ( char * log_target, int enable, char * filename)
{
int ret = 0 ;
ret = setenv ( DLOG_LOG_TARGET_ENV, log_target, enable) ;
if ( ret < 0 )
return - 1 ;
if ( ! strcmp ( log_target, "file" ) )
ret = setenv ( DLOG_LOG_TARGET_FILE_ENV, filename, enable) ;
return ret;
}
static int get_log_level ( void )
{
int ret = DLOG_ERR;
char * log_level = secure_getenv ( DLOG_LOG_LEVEL_ENV) ;
if ( ! log_level)
return ret;
for ( int i = 0 ; i < ARRARY_SIZE ( key_to_str) ; i ++ ) {
if ( ! strcmp ( log_level, key_to_str[ i] . level_str) ) {
ret = key_to_str[ i] . level;
break ;
}
}
return ret;
}
static inline void close_file ( void )
{
fclose ( logContext. file_out) ;
logContext. file_name = NULL ;
}
static void get_log_target ( void )
{
char * target_file = NULL ;
char * log_target = secure_getenv ( DLOG_LOG_TARGET_ENV) ;
int log_target_prev = logContext. log_target;
if ( ! log_target)
return ;
if ( ! strcmp ( log_target, "syslog" ) ) {
if ( log_target_prev != DLOG_TARGET_SYSLOG) {
if ( log_target_prev == DLOG_TARGET_FILE) {
close_file ( ) ;
}
logContext. log_target = DLOG_TARGET_SYSLOG;
}
} else if ( ! strcmp ( log_target, "stderr" ) ) {
if ( log_target_prev != DLOG_TARGET_STDERR) {
if ( log_target_prev == DLOG_TARGET_FILE) {
close_file ( ) ;
}
logContext. log_target = DLOG_TARGET_STDERR;
logContext. file_name = NULL ;
logContext. file_out = stderr ;
}
} else if ( ! strcmp ( log_target, "file" ) ) {
logContext. log_target = DLOG_TARGET_FILE;
target_file = secure_getenv ( DLOG_LOG_TARGET_FILE_ENV) ;
if ( ! target_file) {
target_file = DLOG_LOG_DEFAULT_FILE;
}
if ( log_target_prev == DLOG_TARGET_FILE) {
if ( strcmp ( target_file, logContext. file_name) ) {
close_file ( ) ;
logContext. file_name = target_file;
logContext. file_out = fopen ( target_file, "a" ) ;
if ( ! logContext. file_out) {
logContext. log_target = DLOG_TARGET_STDERR;
logContext. file_name = NULL ;
logContext. file_out = stderr ;
}
}
} else {
logContext. file_name = target_file;
logContext. file_out = fopen ( target_file, "a" ) ;
if ( ! logContext. file_out) {
logContext. log_target = DLOG_TARGET_STDERR;
logContext. file_name = NULL ;
logContext. file_out = stderr ;
}
}
}
}
static void update_context ( void )
{
if ( logContext. log_target == DLOG_TARGET_STDERR)
logContext. file_out = stderr ;
logContext. log_level = get_log_level ( ) ;
get_log_target ( ) ;
memset ( logContext. message_buf, 0 , MESSAGE_SIZE) ;
}
static void gen_prefix ( int level, int target, char * file, int line, char * func, char * format)
{
char line_no[ 256 ] = { 0 } ;
time_t time_now;
char * timestr = ctime ( & time_now) ;
strcat ( logContext. message_buf, timestr) ;
logContext. message_buf[ strlen ( timestr) - 1 ] = '\0' ;
strcat ( logContext. message_buf, ":" ) ;
if ( target == DLOG_TARGET_SYSLOG || target == DLOG_TARGET_FILE) {
switch ( level) {
case DLOG_CRIT:
strcat ( logContext. message_buf, "[CRIT]:" ) ;
strcat ( logContext. message_buf, file) ;
strcat ( logContext. message_buf, ":" ) ;
strcat ( logContext. message_buf, func) ;
strcat ( logContext. message_buf, ":" ) ;
sprintf ( line_no, "%d" , line) ;
strcat ( logContext. message_buf, line_no) ;
strcat ( logContext. message_buf, ":" ) ;
strcat ( logContext. message_buf, format) ;
break ;
case DLOG_ERR:
strcat ( logContext. message_buf, "[ERR]:" ) ;
strcat ( logContext. message_buf, file) ;
strcat ( logContext. message_buf, ":" ) ;
strcat ( logContext. message_buf, func) ;
strcat ( logContext. message_buf, ":" ) ;
sprintf ( line_no, "%d" , line) ;
strcat ( logContext. message_buf, line_no) ;
strcat ( logContext. message_buf, ":" ) ;
strcat ( logContext. message_buf, format) ;
break ;
case DLOG_WARN:
strcat ( logContext. message_buf, "[WARN]:" ) ;
strcat ( logContext. message_buf, file) ;
strcat ( logContext. message_buf, ":" ) ;
strcat ( logContext. message_buf, func) ;
strcat ( logContext. message_buf, ":" ) ;
sprintf ( line_no, "%d" , line) ;
strcat ( logContext. message_buf, line_no) ;
strcat ( logContext. message_buf, ":" ) ;
strcat ( logContext. message_buf, format) ;
break ;
case DLOG_NOTICE:
strcat ( logContext. message_buf, "[NOTICE]:" ) ;
strcat ( logContext. message_buf, file) ;
strcat ( logContext. message_buf, ":" ) ;
strcat ( logContext. message_buf, func) ;
strcat ( logContext. message_buf, ":" ) ;
sprintf ( line_no, "%d" , line) ;
strcat ( logContext. message_buf, line_no) ;
strcat ( logContext. message_buf, ":" ) ;
strcat ( logContext. message_buf, format) ;
break ;
case DLOG_INFO:
strcat ( logContext. message_buf, "[INFO]:" ) ;
strcat ( logContext. message_buf, file) ;
strcat ( logContext. message_buf, ":" ) ;
strcat ( logContext. message_buf, func) ;
strcat ( logContext. message_buf, ":" ) ;
sprintf ( line_no, "%d" , line) ;
strcat ( logContext. message_buf, line_no) ;
strcat ( logContext. message_buf, ":" ) ;
strcat ( logContext. message_buf, format) ;
break ;
case DLOG_DEBUG:
strcat ( logContext. message_buf, "[DEBUG]:" ) ;
strcat ( logContext. message_buf, file) ;
strcat ( logContext. message_buf, ":" ) ;
strcat ( logContext. message_buf, func) ;
strcat ( logContext. message_buf, ":" ) ;
sprintf ( line_no, "%d" , line) ;
strcat ( logContext. message_buf, line_no) ;
strcat ( logContext. message_buf, ":" ) ;
strcat ( logContext. message_buf, format) ;
break ;
case DLOG_PRINTF:
strcat ( logContext. message_buf, file) ;
strcat ( logContext. message_buf, ":" ) ;
strcat ( logContext. message_buf, func) ;
strcat ( logContext. message_buf, ":" ) ;
sprintf ( line_no, "%d" , line) ;
strcat ( logContext. message_buf, line_no) ;
strcat ( logContext. message_buf, ":" ) ;
strcat ( logContext. message_buf, format) ;
break ;
default : ;
}
} else {
switch ( level) {
case DLOG_CRIT:
strcat ( logContext. message_buf, "\033[31m" ) ;
strcat ( logContext. message_buf, "[CRIT]:" ) ;
strcat ( logContext. message_buf, file) ;
strcat ( logContext. message_buf, ":" ) ;
strcat ( logContext. message_buf, func) ;
strcat ( logContext. message_buf, ":" ) ;
sprintf ( line_no, "%d" , line) ;
strcat ( logContext. message_buf, line_no) ;
strcat ( logContext. message_buf, ":" ) ;
strcat ( logContext. message_buf, format) ;
strcat ( logContext. message_buf, "\033[0m" ) ;
break ;
case DLOG_ERR:
strcat ( logContext. message_buf, "\033[31m" ) ;
strcat ( logContext. message_buf, "[ERR]:" ) ;
strcat ( logContext. message_buf, file) ;
strcat ( logContext. message_buf, ":" ) ;
strcat ( logContext. message_buf, func) ;
strcat ( logContext. message_buf, ":" ) ;
sprintf ( line_no, "%d" , line) ;
strcat ( logContext. message_buf, line_no) ;
strcat ( logContext. message_buf, ":" ) ;
strcat ( logContext. message_buf, format) ;
strcat ( logContext. message_buf, "\033[0m" ) ;
break ;
case DLOG_WARN:
strcat ( logContext. message_buf, "\033[33m" ) ;
strcat ( logContext. message_buf, "[WARN]:" ) ;
strcat ( logContext. message_buf, file) ;
strcat ( logContext. message_buf, ":" ) ;
strcat ( logContext. message_buf, func) ;
strcat ( logContext. message_buf, ":" ) ;
sprintf ( line_no, "%d" , line) ;
strcat ( logContext. message_buf, line_no) ;
strcat ( logContext. message_buf, ":" ) ;
strcat ( logContext. message_buf, format) ;
strcat ( logContext. message_buf, "\033[0m" ) ;
break ;
case DLOG_NOTICE:
strcat ( logContext. message_buf, "\033[34m" ) ;
strcat ( logContext. message_buf, "[NOTICE]:" ) ;
strcat ( logContext. message_buf, file) ;
strcat ( logContext. message_buf, ":" ) ;
strcat ( logContext. message_buf, func) ;
strcat ( logContext. message_buf, ":" ) ;
sprintf ( line_no, "%d" , line) ;
strcat ( logContext. message_buf, line_no) ;
strcat ( logContext. message_buf, ":" ) ;
strcat ( logContext. message_buf, format) ;
strcat ( logContext. message_buf, "\033[0m" ) ;
break ;
case DLOG_INFO:
strcat ( logContext. message_buf, "\033[32m" ) ;
strcat ( logContext. message_buf, "[INFO]:" ) ;
strcat ( logContext. message_buf, file) ;
strcat ( logContext. message_buf, ":" ) ;
strcat ( logContext. message_buf, func) ;
strcat ( logContext. message_buf, ":" ) ;
sprintf ( line_no, "%d" , line) ;
strcat ( logContext. message_buf, line_no) ;
strcat ( logContext. message_buf, ":" ) ;
strcat ( logContext. message_buf, format) ;
strcat ( logContext. message_buf, "\033[0m" ) ;
break ;
case DLOG_DEBUG:
strcat ( logContext. message_buf, "\033[36m" ) ;
strcat ( logContext. message_buf, "[DEBUG]:" ) ;
strcat ( logContext. message_buf, file) ;
strcat ( logContext. message_buf, ":" ) ;
strcat ( logContext. message_buf, func) ;
strcat ( logContext. message_buf, ":" ) ;
sprintf ( line_no, "%d" , line) ;
strcat ( logContext. message_buf, line_no) ;
strcat ( logContext. message_buf, ":" ) ;
strcat ( logContext. message_buf, format) ;
strcat ( logContext. message_buf, "\033[0m" ) ;
break ;
case DLOG_PRINTF:
strcat ( logContext. message_buf, file) ;
strcat ( logContext. message_buf, ":" ) ;
strcat ( logContext. message_buf, func) ;
strcat ( logContext. message_buf, ":" ) ;
sprintf ( line_no, "%d" , line) ;
strcat ( logContext. message_buf, line_no) ;
strcat ( logContext. message_buf, ":" ) ;
strcat ( logContext. message_buf, format) ;
break ;
default : ;
}
}
return ;
}
int dlog_debug ( char * file, int line, char * func, char * format, . . . )
{
va_list vlist;
va_start ( vlist, format) ;
update_context ( ) ;
if ( DLOG_DEBUG <= logContext. log_level) {
gen_prefix ( DLOG_DEBUG, logContext. log_target, file, line, func, format) ;
switch ( logContext. log_target) {
case DLOG_TARGET_SYSLOG:
vsyslog ( level_to_syspt[ DLOG_DEBUG] , logContext. message_buf, vlist) ;
break ;
case DLOG_TARGET_STDERR:
case DLOG_TARGET_FILE:
vfprintf ( logContext. file_out, logContext. message_buf, vlist) ;
fflush ( logContext. file_out) ;
break ;
default : ;
}
}
va_end ( vlist) ;
return 0 ;
}
int dlog_info ( char * file, int line, char * func, char * format, . . . )
{
va_list vlist;
va_start ( vlist, format) ;
update_context ( ) ;
if ( DLOG_INFO <= logContext. log_level) {
gen_prefix ( DLOG_INFO, logContext. log_target, file, line, func, format) ;
switch ( logContext. log_target) {
case DLOG_TARGET_SYSLOG:
vsyslog ( level_to_syspt[ DLOG_INFO] , logContext. message_buf, vlist) ;
break ;
case DLOG_TARGET_STDERR:
case DLOG_TARGET_FILE:
vfprintf ( logContext. file_out, logContext. message_buf, vlist) ;
fflush ( logContext. file_out) ;
break ;
default : ;
}
}
va_end ( vlist) ;
return 0 ;
}
int dlog_notice ( char * file, int line, char * func, char * format, . . . )
{
va_list vlist;
va_start ( vlist, format) ;
update_context ( ) ;
if ( DLOG_NOTICE <= logContext. log_level) {
gen_prefix ( DLOG_NOTICE, logContext. log_target, file, line, func, format) ;
switch ( logContext. log_target) {
case DLOG_TARGET_SYSLOG:
vsyslog ( level_to_syspt[ DLOG_NOTICE] , logContext. message_buf, vlist) ;
break ;
case DLOG_TARGET_STDERR:
case DLOG_TARGET_FILE:
vfprintf ( logContext. file_out, logContext. message_buf, vlist) ;
fflush ( logContext. file_out) ;
break ;
default : ;
}
}
va_end ( vlist) ;
return 0 ;
}
int dlog_warn ( char * file, int line, char * func, char * format, . . . )
{
va_list vlist;
va_start ( vlist, format) ;
update_context ( ) ;
if ( DLOG_WARN <= logContext. log_level) {
gen_prefix ( DLOG_WARN, logContext. log_target, file, line, func, format) ;
switch ( logContext. log_target) {
case DLOG_TARGET_SYSLOG:
vsyslog ( level_to_syspt[ DLOG_WARN] , logContext. message_buf, vlist) ;
break ;
case DLOG_TARGET_STDERR:
case DLOG_TARGET_FILE:
vfprintf ( logContext. file_out, logContext. message_buf, vlist) ;
fflush ( logContext. file_out) ;
break ;
default : ;
}
}
va_end ( vlist) ;
return 0 ;
}
int dlog_error ( char * file, int line, char * func, char * format, . . . )
{
va_list vlist;
va_start ( vlist, format) ;
update_context ( ) ;
if ( DLOG_ERR <= logContext. log_level) {
gen_prefix ( DLOG_ERR, logContext. log_target, file, line, func, format) ;
switch ( logContext. log_target) {
case DLOG_TARGET_SYSLOG:
vsyslog ( level_to_syspt[ DLOG_ERR] , logContext. message_buf, vlist) ;
break ;
case DLOG_TARGET_STDERR:
case DLOG_TARGET_FILE:
vfprintf ( logContext. file_out, logContext. message_buf, vlist) ;
fflush ( logContext. file_out) ;
break ;
default : ;
}
}
va_end ( vlist) ;
return 0 ;
}
int dlog_crit ( char * file, int line, char * func, char * format, . . . )
{
va_list vlist;
va_start ( vlist, format) ;
update_context ( ) ;
if ( DLOG_CRIT <= logContext. log_level) {
gen_prefix ( DLOG_CRIT, logContext. log_target, file, line, func, format) ;
switch ( logContext. log_target) {
case DLOG_TARGET_SYSLOG:
vsyslog ( level_to_syspt[ DLOG_CRIT] , logContext. message_buf, vlist) ;
break ;
case DLOG_TARGET_STDERR:
case DLOG_TARGET_FILE:
vfprintf ( logContext. file_out, logContext. message_buf, vlist) ;
fflush ( logContext. file_out) ;
break ;
default : ;
}
}
va_end ( vlist) ;
return 0 ;
}
int dlog_printf ( char * file, int line, char * func, char * format, . . . )
{
va_list vlist;
va_start ( vlist, format) ;
update_context ( ) ;
gen_prefix ( DLOG_PRINTF, logContext. log_target, file, line, func, format) ;
switch ( logContext. log_target) {
case DLOG_TARGET_SYSLOG:
vsyslog ( level_to_syspt[ DLOG_DEBUG] , logContext. message_buf, vlist) ;
break ;
case DLOG_TARGET_STDERR:
case DLOG_TARGET_FILE:
vfprintf ( logContext. file_out, logContext. message_buf, vlist) ;
fflush ( logContext. file_out) ;
break ;
default : ;
}
va_end ( vlist) ;
return 0 ;
}
int dlog_printf_exit ( char * file, int line, char * func, char * format, . . . )
{
va_list vlist;
va_start ( vlist, format) ;
update_context ( ) ;
gen_prefix ( DLOG_PRINTF, logContext. log_target, file, line, func, format) ;
switch ( logContext. log_target) {
case DLOG_TARGET_SYSLOG:
vsyslog ( level_to_syspt[ DLOG_DEBUG] , logContext. message_buf, vlist) ;
break ;
case DLOG_TARGET_STDERR:
case DLOG_TARGET_FILE:
vfprintf ( logContext. file_out, logContext. message_buf, vlist) ;
fflush ( logContext. file_out) ;
break ;
default : ;
}
va_end ( vlist) ;
exit ( 0 ) ;
return 0 ;
}
# include "dlog.h"
# include <stdio.h>
int main ( int argc, char * argv[ ] )
{
int a = 100 , b = 1000 ;
PRINTF ( "value a is %d, b is %d\n" , a, b) ;
DEBUG ( "value a is %d, b is %d\n" , a, b) ;
INFO ( "value a is %d, b is %d\n" , a, b) ;
NOTICE ( "value a is %d, b is %d\n" , a, b) ;
WARN ( "value a is %d, b is %d\n" , a, b) ;
ERROR ( "value a is %d, b is %d\n" , a, b) ;
CRIT ( "value a is %d, b is %d\n" , a, b) ;
}
. phony: all clean
SRCS: = test_dlog. c dlog. c
TARGET: = test_dlog
all: $( TARGET)
$( TARGET) : $( SRCS)
gcc - o $@ $^
clean:
rm - f $( TARGET)
# export DLOG_LOG_TARGET= stderr
# export DLOG_LOG_LEVEL= none
# . / test_dlog
Thu Jan 1 08 : 00 : 00 1970 : test_dlog. c: main: 8 : value a is 100 , b is 1000
# export DLOG_LOG_TARGET= stderr
# export DLOG_LOG_LEVEL= debug
头文件中简单的DEBUG
# ifndef __DECOD_DEBUG_H__
# define __DECOD_DEBUG_H__
# include <stdio.h>
enum {
E_INFO = 0 ,
E_NOTICE,
E_DEBUG,
E_WARRN,
E_ERROR,
} ;
# define LOG_LEVEL 0
# ifdef LOG_LEVEL
# define LOG_OUT ( level, color, tag, str, . . . ) ( \
( tag >= LOG_LEVEL) ? \
fprintf ( stdout , color tag "%s:%d:" \
str "\e[0m" , __FILE__ , __LINE__ , \
## __VA_ARGS__) : 0 ) ;
# else
# define LOG_OUT ( tag, color, str, . . . )
# endif
# define INFO ( a, . . . ) LOG_OUT ( E_INOF, "\e[1;32m" , "[INFO]" , a, ## __VA_ARGS__)
# define NOTICE ( a, . . . ) LOG_OUT ( E_NOTICE, "\e[1;43m" , "[NOTICE]:" , a, ## __VA_ARGS__)
# define DEBUG ( a, . . . ) LOG_OUT ( E_DEBUG, "\e[1;42m" , "[DEBUG]:" , a, ## __VA_ARGS__)
# define WARRN ( a, . . . ) LOG_OUT ( E_WARRN, "\e[1;34m" , "[WARRN]:" , a, ## __VA_ARGS__)
# define ERROR ( a, . . . ) LOG_OUT ( E_ERROR, "\e[1;41m" , "[ERROR]:" , a, ## __VA_ARGS__)
# define INFO_EXIT ( . . . ) do { INFO ( __VA_ARGS__) ; exit ( 0 ) ; } while ( 0 )
# endif