Libevent-2.1.8源码分析——日志消息

1. 概述

与许多开源库类似,libevent也提供了日志的支持。libevent提供的日志支持可以记录内部的错误和警告,并且这些日志仅libevent内部使用的,应用程序无法使用。值得一提的是,libevent提供的日志支持相对简单。默认配置下这些消息被写向标志错误输出(stderr),当然也提供定制日志函数覆盖默认的行为。对于在应用程序中想使用日志库的话,推荐glog、log4cplus、log4cpp、log4cxx等开源的日志库。

2. libevent 日志 API

在使用libevent中,我们可以看到日志API接口,这些日志API声明在time-internal.h中,表明是内部使用的。
void event_err(int eval, const char *fmt, ...) EV_CHECK_FMT(2,3) EV_NORETURN;
void event_warn(const char *fmt, ...) EV_CHECK_FMT(1,2);
void event_sock_err(int eval, evutil_socket_t sock, const char *fmt, ...) EV_CHECK_FMT(3,4) EV_NORETURN;
void event_sock_warn(evutil_socket_t sock, const char *fmt, ...) EV_CHECK_FMT(2,3);
void event_errx(int eval, const char *fmt, ...) EV_CHECK_FMT(2,3) EV_NORETURN;
void event_warnx(const char *fmt, ...) EV_CHECK_FMT(1,2);
void event_msgx(const char *fmt, ...) EV_CHECK_FMT(1,2);
void event_debugx_(const char *fmt, ...) EV_CHECK_FMT(1,2);

void event_logv_(int severity, const char *errstr, const char *fmt, va_list ap)
	EV_CHECK_FMT(3,0);
关于宏EV_CHECK_FMT,感兴趣的可以参考我的另一篇博客 __attribute__((format(printf, a, b)))。

事实上从源码中我们可以看出event_err、event_warn等API最终是通过调用event_logv_实现的。
void
event_logv_(int severity, const char *errstr, const char *fmt, va_list ap)
{
	char buf[1024];
	size_t len;

	if (severity == EVENT_LOG_DEBUG && !event_debug_get_logging_mask_())
		return;

	if (fmt != NULL)
		evutil_vsnprintf(buf, sizeof(buf), fmt, ap);
	else
		buf[0] = '\0';

	if (errstr) {
		len = strlen(buf);
		if (len < sizeof(buf) - 3) {
			evutil_snprintf(buf + len, sizeof(buf) - len, ": %s", errstr);
		}
	}

	event_log(severity, buf);
}
  • severity:日志的级别。libevent支持4个级别的日志:EVENT_LOG_DEBUG、EVENT_LOG_MSG、EVENT_LOG_WARN和EVENT_LOG_ERR。
  • errstr:附带的错误消息。
  • fmt:类似于printf中的格式化输出格式。
  • ap:可变参数。
如果日志级别是EVENT_LOG_DEBUG并且没有开启日志debug则,不会有日志的输出。如果fmt不为NULL,则按照fmt给的输出格式进行对于的格式输出,如果errstr不为NULL的话,会附带上错误信息。最终,调用的是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);
	}
}
这个就非常的简单了,如果有定制日志的处理函数则使用我们的日志处理函数,否则按照固定的格式输出到标准输出。
定制自己的日志回调函数时通过下面这个API实现的:
void
event_set_log_callback(event_log_cb cb)
{
	log_fn = cb;
}
event_log_cb类型其实就是某个函数指针:
typedef void (*event_log_cb)(int severity, const char *msg);

3. 错误处理

同样的与大部分日志库都提供的功能类似。libevent也提供相应的错误处理。如果发生了某个致命的错误,那么我们希望的处理是保留日志消息并调用相应的错误处理又或者终止程序。
libevent中的错误处理是:在输出对于的日志消息后,如果应用程序提供相应的错误处理,那么就会运行该错误处理,否则终止程序。
static void
event_exit(int errcode)
{
	if (fatal_fn) {
		fatal_fn(errcode);
		exit(errcode); /* should never be reached */
	} else if (errcode == EVENT_ERR_ABORT_)
		abort();
	else
		exit(errcode);
}

4. 配置自己的日志函数

#include 
#include 
#include 
#include 

#if _MSC_VER  
#define snprintf _snprintf  
#endif 

void log_callback(int severity, const char *msg)
{
	char szBuffer[512];

	FILE *pFd = fopen("./log.txt", "ab+");
	if (pFd == NULL)
		return;

	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;
		}
	
	snprintf(szBuffer, sizeof(szBuffer), "[%s]:%s", severity_str, msg);

	(void)fwrite(szBuffer, 1, strlen(szBuffer), pFd);

	fclose(pFd);
}

int main(void)
{
	event_set_log_callback(log_callback);
	//dong something...
	return 0;
}


你可能感兴趣的:(libevent源码分析)