acl 日志记录方式介绍

      在使用 acl 库编写应用过程当中,记录日志是一个很是重要的过程,acl 从几个层面提供了日志的不一样记录方式。在 acl 的 C 库部分(lib_acl.a),有三个源文件与日志记录相关:acl_msg.c/acl_msg.h, acl_mylog.c/acl_mylog.h, acl_debug.c/acl_debug.h。其中,acl_mylog.c 是真正记录日志的源文件,acl_msg.c 则是在 acl_mylog.c 基础之上的二次封装,acl_debug.c 是在 acl_msg.c 基础之上的再次封装。下面根据此三个日志源文件从三个层次描述日志记录的过程。安全

 

      1、ac_mylog.c/acl_mylog.h网络

      打开 acl_mylog.h 头文件,能够看到主要有三个函数:acl_open_log(打开日志文件),acl_write_to_log(写日志)以及acl_close_log(关闭日志)---(这三个函数是最基础的日志记录过程,固然咱们没必要直接使用)。该库支持两类日志记录方式:一、本地文件记录方式,二、与 syslog-ng 结合的网络日志记录方式。本地文件记录方式是 acl 日志库对外提供的最简单的日志记录方式,此方式不依赖于第三方日志库,但不该用在生产环境中,由于该方式不支持日志回滚等高级特性,为了便于生产上使用,因此产生了第二种方式(与 syslog-ng 结合),查看日志打开接口(以下):svn

/**
 * 打开日志文件
 * @param recipients {const char*} 日志接收器列表,由 "|" 分隔,接收器
 *  能够是本地文件或远程套接口,如:
 *  /tmp/test.log|UDP:127.0.0.1:12345|TCP:127.0.0.1:12345|UNIX:/tmp/test.sock
 *  该配置要求将全部日志同时发给 /tmp/test.log, UDP:127.0.0.1:12345,
 *  TCP:127.0.0.1:12345 和 UNIX:/tmp/test.sock 四个日志接收器对象
 * @param plog_pre {const char*} 日志记录信息前的提示信息,建议用进程
 *  名填写此值
 */
ACL_API int acl_open_log(const char *recipients, const char *plog_pre);

       从上面的函数声明能够看出,acl 的日志记录容许同时输出至多个日志管道中(最简单的方式就是直接写入本地磁盘文件:/tmp/test.log),同时更应看到,其中有三个奇怪的日志文件表达方式:UDP:IP:PORT, TCP:IP:PORT, UNIX:/xxx,其实这三种方式均是与 syslog-ng 相关,即分别表示:函数

      一、以 UDP 方式发送日志至 syslog-ng;ui

      二、以 TCP 方式发送日志至 syslog-ng;.net

      三、以 UNIX 域套接字方式发送日志至 syslog-ng。
      由于日志管理是一个很是复杂的过程,因此在 acl 除了提供最简单的日志文件记录外,更建议用户将日志输出至 syslog-ng 中(做者本身的项目也每每是这样作的)。线程

 

      2、acl_msg.c/acl_msg.hdebug

      该日志库提供了更为高级的日志记录方法,不只提供了灵活的日志记录函数,同时还容许用户注册本身的日志记录函数库,该日志库主要函数接口以下:调试

/**
 * 日志打开函数
 * @param log_file {const char*} 日志接收者集合,由 "|" 分隔,接收器
 *  能够是本地文件或远程套接口,如:
 *  /tmp/test.log|UDP:127.0.0.1:12345|TCP:127.0.0.1:12345|UNIX:/tmp/test.sock
 *  该配置要求将全部日志同时发给 /tmp/test.log, UDP:127.0.0.1:12345,
 *  TCP:127.0.0.1:12345 和 UNIX:/tmp/test.sock 四个日志接收器对象
 * @param plog_pre {const char*} 日志记录信息前的提示信息,建议用进程
 * @param info_pre {const char*} 日志记录信息前的提示信息
 */
ACL_API void acl_msg_open(const char *log_file, const char *info_pre);

/**
 * 关闭日志函数
 */
ACL_API void acl_msg_close(void);

       上面是日志打开与关闭的函数,看上去算是相对简单。下面是几个日志记录的函数接口:日志

/**
 * 通常级别日志信息记录函数
 * @param fmt {const char*} 参数格式
 * @param ... 变参序列
 */
#ifdef	WIN32
ACL_API void acl_msg_info(const char *fmt,...);
#else
ACL_API void __attribute__((format(printf,1,2)))
	acl_msg_info(const char *fmt,...);
#endif

/**
 * 警告级别日志信息记录函数
 * @param fmt {const char*} 参数格式
 * @param ... 变参序列
 */
#ifdef	WIN32
ACL_API void acl_msg_warn(const char *fmt,...);
#else
ACL_API void __attribute__((format(printf,1,2)))
	acl_msg_warn(const char *fmt,...);
#endif

/**
 * 错误级别日志信息记录函数
 * @param fmt {const char*} 参数格式
 * @param ... 变参序列
 */
#ifdef	WIN32
ACL_API void acl_msg_error(const char *fmt,...);
#else
ACL_API void __attribute__((format(printf,1,2)))
	acl_msg_error(const char *fmt,...);
#endif

/**
 * 致命级别日志信息记录函数
 * @param fmt {const char*} 参数格式
 * @param ... 变参序列
 */
#ifdef	WIN32
ACL_API void acl_msg_fatal(const char *fmt,...);
#else
ACL_API void __attribute__((format(printf,1,2)))
	acl_msg_fatal(const char *fmt,...);
#endif

/**
 * 恐慌级别日志信息记录函数
 * @param fmt {const char*} 参数格式
 * @param ... 变参序列
 */
#ifdef	WIN32
ACL_API void acl_msg_panic(const char *fmt,...);
#else
ACL_API void __attribute__((format(printf,1,2)))
	acl_msg_panic(const char *fmt,...);
#endif

       能够看到,这些函数的使用方式与 printf 相似,另外,在 UNIX 下使用 GCC 编译时前面还有一个修饰符:__attribute__((format(printf,m,n))),这主要是方便 gcc 编译器针对变参进行语法检查(你们应该知道变参是如此方便灵活而又如此容易出错)。

      为了方便程序开发过程当中的调试,下面的函数当用户未调用 acl_msg_open 打开日志而直接使用 acl_msg_xxx 写日志时,决定是否将日志信息输出至屏幕(这个函数应该在程序初始化时调用):

/**
 * 当未调用 acl_msg_open 方式打开日志时,调用了 acl_msg_info/error/fatal/warn
 * 的操做,是否容许信息输出至标准输出屏幕上,经过此函数来设置该开关,该开关
 * 仅影响是否须要将信息输出至终端屏幕而不影响是否输出至文件中
 * @param onoff {int} 非 0 表示容许输出至屏幕
 */
ACL_API void acl_msg_stdout_enable(int onoff);

       前面曾说过,acl 的日志库还容许用户使用本身的日志记录过程,但要求用户必须在程序初始化时注册本身的日志处理函数,以下:

/**
 * 在打开日志前调用此函数注册应用本身的日志打开函数、日志关闭函数、日志记录函数
 * @param open_fn {ACL_MSG_OPEN_FN} 自定义日志打开函数
 * @param close_fn {ACL_MSG_CLOSE_FN} 自定义日志关闭函数
 * @param write_fn {ACL_MSG_WRITE_FN} 自定义日志记录函数
 * @param ctx {void*} 自定义参数
 */
ACL_API void acl_msg_register(ACL_MSG_OPEN_FN open_fn, ACL_MSG_CLOSE_FN close_fn,
        ACL_MSG_WRITE_FN write_fn, void *ctx);

     调用此函数后,之后的日志记录过程(即当用户调用:acl_msg_xxx 相关过程时)的内容便输出便由用户的日志库控制。

 

      除了以上主要的日志函数接口,在 acl_msg 中还提供了如下几个函数,便于用户知晓程序出错缘由:

/**
 * 得到上次系统调用出错时的错误描述信息,该函数内部采用了线程局部变量,因此是线程
 * 安全的,但使用起来更简单些
 * @return {const char *} 返回错误提示信息 
 */
ACL_API const char *acl_last_serror(void);

/**
 * 得到上次系统调用出错时的错误号
 * @return {int} 错误号
 */
ACL_API int acl_last_error(void);

 

      3、acl_debug.c/acl_debug.h

      该日志函数库是在 acl_msg 之上的再一次封装,该库的思想来源于 squid 的日志记录方式,能够将日志分红不一样的类别,每个类别又分红不一样的级别,这样用户就能够很是方便地经过配置文件来记录不一样类别的不一样级别的日志信息了。在程序初始化时需先调用如此函数:

/**
 * 初始化日志调试调用接口
 * @param pStr {const char*} 调试类别(建议值在100至1000之间)标签及级别字符串,
 *  格式: 1,1; 2,10; 3,8...  or 1:1; 2:10; 3:8...
 */
ACL_API void acl_debug_init(const char *pStr);

/**
 * 初始化日志调试调用接口
 * @param pStr {const char*} 调试标签及级别字符串,
 *  格式: 1,1; 2,10; 3,8...  or 1:1; 2:10; 3:8...
 * @param max_debug_level {int} 最大调试标签值
 */
ACL_API void acl_debug_init2(const char *pStr, int max_debug_level);

       其中,第一个参数是一个由日志记录类别与级别组成的字符串,格式为:类别1:最大记录级别, 类别2:最大记录级别, ...。例如:100:2; 102:3; 103:4,其含义是日志将会记录类别为 100 的全部级别值小于二、类别为 101 的全部级别值小于 3 以及类别为 103 的全部级别值小于 4 的日志信息。关于记录类别须要注意:类别值最好是 >= 100,且 < 1000(当使用 acl_debug_init2 初始化时只要类别值 >= 100 便可,由于第二个参数指定了最大类别值),这是由于 acl 库内部一些保留的类别值都在 0 -- 100 之间。

       那么具体的使用这些类别与级别记录日志的接口是什么呢?以下所示:

/**
 * 日志调试宏接口
 * @param SECTION {int} 调试标签值
 * @param LEVEL {int} 对应于SECTION调试标签的级别
 */
#define acl_debug(SECTION, LEVEL) \
	!acl_do_debug((SECTION), (LEVEL)) ? (void) 0 : acl_msg_info

        看到了吧,用户其实只须要调用一个宏便可,以下面的例子: 

/* 初始化日志类别记录 */
	const char *str = "101:2; 103:4; 105:3";
	/* 记录全部类别值为 101 级别小于等于 二、类别值为 102 级别小于等于 四、类别值为 105 级别小于等于 3 的日志内容 */
	acl_debug_init(str);

	......
	/* 下面的日志因符合类别值 101 级别值 <= 2 而被记录 */
	acl_debug(101, 2)("%s(%d): log time: %ld", __FILE__, __LINE__, time(NULL));

	/* 下面日志符合类别 105 的记录级别 */
	acl_debug(105, 1)("%s(%d): log time: %ld", __FILE__, __LINE__, time(NULL));

	/* 下面的日志因不符合类别值 103 的记录级别条件而被忽略 */
	acl_debug(103, 5)("%s(%d): log time: %ld", __FILE__, __LINE__, time(NULL));

	/* 下面日志的类别值 102 因不存在而被忽略 */
	acl_debug(102, 1)("%s(%d): log time: %ld", __FILE__, __LINE__, time(NULL));

 

      此外,为了方便,还能够传给 acl_debug_init 的参数写为:"all:1",意思是全部类别的级别值 <= 1 的日志都将被记录,以下面的内容都会被记录:

 

acl_debug_init("all:1");
......
	acl_debug(100, 1)("%s(%d): log time: %ld", __FILE__, __LINE__, time(NULL));
	acl_debug(101, 1)("%s(%d): log time: %ld", __FILE__, __LINE__, time(NULL));
	acl_debug(101, 0)("%s(%d): log time: %ld", __FILE__, __LINE__, time(NULL));
	acl_debug(102, 1)("%s(%d): log time: %ld", __FILE__, __LINE__, time(NULL));
	acl_debug(103, 1)("%s(%d): log time: %ld", __FILE__, __LINE__, time(NULL));
	acl_debug(104, 1)("%s(%d): log time: %ld", __FILE__, __LINE__, time(NULL));
	acl_debug(105, 1)("%s(%d): log time: %ld", __FILE__, __LINE__, time(NULL));
        ......

 

       ok,有关日志  acl 日志记录函数就先写这些,使用者能够根据项目须要采用不一样的日志记录方式。

 

      参考:

      本文地址:http://zsxxsz.iteye.com/blog/1893115

      更多文章:http://zsxxsz.iteye.com/

      下载:http://sourceforge.net/projects/acl/

      svn:svn checkout svn://svn.code.sf.net/p/acl/code/trunk acl-code

      QQ 群:242722074

相关文章
相关标签/搜索