libevent 定制——libevent定制日志函数

libevent 定制日志函数

libevent在默认情况下,会将日志信息输出到终端上。这当然就不利于日后的观察。为此,libevent允许用户定制自己的日志回调函数。所有的日志函数在最后输出信息时,都会调用日志回调函数的。所以用户可以通过定制自己的日志回调函数(在回调函数中把信息输出到一个文件上),方便日后的查看。定制回调函数就像设置自己信号处理函数那样,设置一个日志回调函数。当有日志时,libevent库就会调用这个日志回调函数。

回调函数的类型

/**
  A callback function used to intercept Libevent's log messages.

  @see event_set_log_callback
 */
typedef void (*event_log_cb)(int severity, const char * msg);

第一个参数severity是日志级别类型,有下面这些:

/** @name Log severities
 */
/**@{*/
#define EVENT_LOG_DEBUG 0
#define EVENT_LOG_MSG   1
#define EVENT_LOG_WARN  2
#define EVENT_LOG_ERR   3
/**@}*/

/* Obsolete names: these are deprecated, but older programs might use them.
 * They violate the reserved-identifier namespace. */
#define _EVENT_LOG_DEBUG EVENT_LOG_DEBUG
#define _EVENT_LOG_MSG EVENT_LOG_MSG
#define _EVENT_LOG_WARN EVENT_LOG_WARN
#define _EVENT_LOG_ERR EVENT_LOG_ERR

第二个参数是需要打印的日志。

设置日志回调函数

在文件include/event2/event.h中:

/**
  Redirect Libevent's log messages.

  @param cb a function taking two arguments: an integer severity between
     EVENT_LOG_DEBUG and EVENT_LOG_ERR, and a string.  If cb is NULL,
     then the default log is used.

  NOTE: The function you provide *must not* call any other libevent
  functionality.  Doing so can produce undefined behavior.
  */
EVENT2_EXPORT_SYMBOL
void event_set_log_callback(event_log_cb cb);

该函数的定义在文件log.c中:

static event_log_cb log_fn = NULL;

void
event_set_log_callback(event_log_cb cb)
{
	log_fn = cb;
}

可以看到,它是通过定义一个全局函数指针变量来保存用户在日志定制函数中传入的回调函数参数。

总结

要覆盖 libevent 的日志行为,编写匹配 event_log_cb 签名的定制函数,将其作为参数传递给 event_set_log_callback()。随后 libevent 在日志信息的时候,将会把信息传递给你提供的函数。再次调用 event_set_log_callback(),传递参数 NULL,就可以恢复默认行为。

libevent的日志API的使用也是挺简单的。首先,使用者确定要记录的日志的级别和错误类型,然后调用对应的日志函数。有下面这些可供选择的日志函数。

EVENT2_EXPORT_SYMBOL
void event_err(int eval, const char *fmt, ...) EV_CHECK_FMT(2,3) EV_NORETURN;
EVENT2_EXPORT_SYMBOL
void event_warn(const char *fmt, ...) EV_CHECK_FMT(1,2);
EVENT2_EXPORT_SYMBOL
void event_sock_err(int eval, evutil_socket_t sock, const char *fmt, ...) EV_CHECK_FMT(3,4) EV_NORETURN;
EVENT2_EXPORT_SYMBOL
void event_sock_warn(evutil_socket_t sock, const char *fmt, ...) EV_CHECK_FMT(2,3);
EVENT2_EXPORT_SYMBOL
void event_errx(int eval, const char *fmt, ...) EV_CHECK_FMT(2,3) EV_NORETURN;
EVENT2_EXPORT_SYMBOL
void event_warnx(const char *fmt, ...) EV_CHECK_FMT(1,2);
EVENT2_EXPORT_SYMBOL
void event_msgx(const char *fmt, ...) EV_CHECK_FMT(1,2);
EVENT2_EXPORT_SYMBOL
void event_debugx_(const char *fmt, ...) EV_CHECK_FMT(1,2);

EVENT2_EXPORT_SYMBOL
void event_logv_(int severity, const char *errstr, const char *fmt, va_list ap)
	EV_CHECK_FMT(3,0);

这些函数都是声明在log-internal.h文件中。所以用户并不能使用之,这些函数都是libevent内部使用的。

这些函数最终都会调用event_log这个函数:

static void
event_log(int severity, const char *msg)
{
	if (log_fn)
		log_fn(severity, msg);
	else {
		const char *severity_str;
		switch (severity) {
		case EVENT_LOG_DEBUG:
			severity_str = "debug";
			break;
		case EVENT_LOG_MSG:
			severity_str = "msg";
			break;
		case EVENT_LOG_WARN:
			severity_str = "warn";
			break;
		case EVENT_LOG_ERR:
			severity_str = "err";
			break;
		default:
			severity_str = "???";
			break;
		}
		(void)fprintf(stderr, "[%s] %s\n", severity_str, msg);
	}
}

event_log函数中可以看到,当函数指针log_fn不用NULL时,就调用log_fn指向的函数。否则就直接向stderr输出日志信息。所以,设置自己的日志回调函数后,如果想恢复libevent默认的日志回调函数,只需再次调用event_set_log_callback函数,参数设置为NULL即可。

libevent库运行的时候有可能会致命发生错误,此时,libevent的默认行为是调用 exit()或者 abort(),退出正在运行的进程。。同日志处理一样,用户也是可以为 libevent 提供在退出时应该调用的函数,覆盖默认行为。错误处理函数的格式和定制函数如下:

/**
   A function to be called if Libevent encounters a fatal internal error.

   @see event_set_fatal_callback
 */
typedef void (*event_fatal_cb)(int err);

/**
 Override Libevent's behavior in the event of a fatal internal error.

 By default, Libevent will call exit(1) if a programming error makes it
 impossible to continue correct operation.  This function allows you to supply
 another callback instead.  Note that if the function is ever invoked,
 something is wrong with your program, or with Libevent: any subsequent calls
 to Libevent may result in undefined behavior.

 Libevent will (almost) always log an EVENT_LOG_ERR message before calling
 this function; look at the last log message to see why Libevent has died.
 */
EVENT2_EXPORT_SYMBOL
void event_set_fatal_callback(event_fatal_cb cb);

首先定义 libevent 在遇到致命错误时应该调用的函数,将其传递给event_set_fatal_callback()。随后 libevent 在遇到致命错误时将调用你提供的函数。

定制函数的内部实现原理和前面的日志处理函数一样,都是通过一个全局函数指针变量存储用户的错误处理函数。

在默认情况下,libevent处理这些致命错误时会粗暴地杀死程序,但大多数情况下,libevent在调用这个致命处理函数前都会调用前面的日志记录函数,其级别是_EVENT_LOG_ERR。此时,虽然程序突然死了,但还是可以在日志中找到一些信息。

同时,用户的错误处理函数不应该将控制返回到 libevent,这样做可能导致不确定的行为。为了避免崩溃,libevent 还是会退出。你的函数被不应该调用其它 libevent 函数。

如果要定制自己的日志处理函数和错误处理函数,那么应该在程序的一开始位置就进行定制。
在用户提供的 event_log_cb 回调函数中调用 libevent 函数是不安全的

posted @ 2023-09-18 23:59  main_c  阅读(1)  评论(0)    收藏  举报  来源