一、简介
zlog是一个高可靠性、高性能、线程安全、灵活、概念清晰的纯C日志函数库,在效率、功能、安全性上大大超过了log4c,并且是用c写成的,具有比较好的通用性。
二、安装
下载
https://github.com/HardySimpson/zlog
https://github.com/HardySimpson/zlog/archive/latest-stable.tar.gz https://github.com/downloads/HardySimpson/zlog/zlog-latest-stable.tar.gz
三、实例
参考:
源码目录:test,包括了大量使用示例,如下:
四、系统日志
参考:
示例:
#include <syslog.h> int main(int argc, char **argv) { openlog("MyMsgMARK", LOG_CONS | LOG_PID, 0); syslog(LOG_INFO, "This is a syslog test message generated by program '%s'\n", argv[0]); closelog(); return 0; }
编译
gcc -g -o test test.c
运行
五、Apache 日志处理分析
Apache通过调用ap_log_XXX函数进行日志记录,此处以ap_log_perror为例进行分析
Apache启动时通过httpd/modules/core/mod_so.c:load_module函数进行DSO模块加载,加载成功后将调用ap_log_perror函数记录模块加载日志
266 ap_log_perror(APLOG_MARK, APLOG_DEBUG, 0, cmd->pool, APLOGNO(01575)"loaded module %s from %s", modname, module_file);
ap_log_perror函数事实上是一个宏,定义在文件:/httpd/include/http_log.h,如下:
412 #define ap_log_perror ap_log_perror_
1409 AP_DECLARE(void) ap_log_perror_(const char *file, int line, int module_index, 1410 int level, apr_status_t status, apr_pool_t *p, 1411 const char *fmt, ...) 1412 { 1413 va_list args; 1414 1415 va_start(args, fmt); 1416 log_error_core(file, line, module_index, level, status, NULL, NULL, NULL, 1417 p, fmt, args); 1418 va_end(args); 1419 }
函数log_error_core,逻辑如下:
1、判断请求结构r是否为NULL,若不为空则:c = r->connection
2、判断服务器结构s是否为NULL,若为空则:logf = stderr_log,此处s为NULL
3、填充结构日志info
4、调用do_errorlog_default函数拼接要写入日志的内容并暂存在buf中,并返回相应长度len
5、调用write_logline函数进行实际的日志输出
6、运行日志钩子error_log
static void log_error_core(const char *file, int line, int module_index, int level, apr_status_t status, const server_rec *s, const conn_rec *c, const request_rec *r, apr_pool_t *pool, const char *fmt, va_list args) { char errstr[MAX_STRING_LEN]; apr_file_t *logf = NULL; int level_and_mask = level & APLOG_LEVELMASK; const request_rec *rmain = NULL; core_server_config *sconf = NULL; ap_errorlog_info info; /* do we need to log once-per-req or once-per-conn info? */ int log_conn_info = 0, log_req_info = 0; apr_array_header_t **lines = NULL; int done = 0; int line_number = 0; if (r) { AP_DEBUG_ASSERT(r->connection != NULL); c = r->connection; } if (s == NULL) { /* * If we are doing stderr logging (startup), don't log messages that are * above the default server log level unless it is a startup/shutdown * notice */ #ifndef DEBUG if ((level_and_mask != APLOG_NOTICE) && (level_and_mask > ap_default_loglevel)) { return; } #endif logf = stderr_log; } else { int configured_level = r ? ap_get_request_module_loglevel(r, module_index) : c ? ap_get_conn_server_module_loglevel(c, s, module_index) : ap_get_server_module_loglevel(s, module_index); if (s->error_log) { /* * If we are doing normal logging, don't log messages that are * above the module's log level unless it is a startup/shutdown notice */ if ((level_and_mask != APLOG_NOTICE) && (level_and_mask > configured_level)) { } else { /* * If we are doing syslog logging, don't log messages that are * above the module's log level (including a startup/shutdown notice) */ if (level_and_mask > configured_level) { } } /* the faked server_rec from mod_cgid does not have s->module_config */ sconf = ap_get_core_module_config(s->module_config); if (c && !c->log_id) { add_log_id(c, NULL); if (sconf->error_log_conn && sconf->error_log_conn->nelts > 0) log_conn_info = 1; } if (r) { if (r->main) rmain = r->main; else rmain = r; if (!rmain->log_id) { /* XXX: do we need separate log ids for subrequests? */ if (sconf->error_log_req && sconf->error_log_req->nelts > 0) log_req_info = 1; /* * XXX: potential optimization: only create log id if %L is * XXX: actually used */ add_log_id(c, rmain); } } } } info.s = s; info.c = c; info.pool = pool; info.file = NULL; info.line = 0; info.status = 0; info.using_syslog = (logf == NULL); info.startup = ((level & APLOG_STARTUP) == APLOG_STARTUP); info.format = fmt; while (!done) { apr_array_header_t *log_format; int len = 0, errstr_start = 0, errstr_end = 0; /* XXX: potential optimization: format common prefixes only once */ if (log_conn_info) { /* once-per-connection info */ if (line_number == 0) { lines = (apr_array_header_t **)sconf->error_log_conn->elts; info.r = NULL; info.rmain = NULL; info.level = -1; info.module_index = APLOG_NO_MODULE; } log_format = lines[line_number++]; if (line_number == sconf->error_log_conn->nelts) { /* this is the last line of once-per-connection info */ line_number = 0; log_conn_info = 0; } } else if (log_req_info) { /* once-per-request info */ if (line_number == 0) { lines = (apr_array_header_t **)sconf->error_log_req->elts; info.r = rmain; info.rmain = rmain; info.level = -1; info.module_index = APLOG_NO_MODULE; } log_format = lines[line_number++]; if (line_number == sconf->error_log_req->nelts) { /* this is the last line of once-per-request info */ line_number = 0; log_req_info = 0; } } else { /* the actual error message */ info.r = r; info.rmain = rmain; info.level = level_and_mask; info.module_index = module_index; info.file = file; info.line = line; info.status = status; log_format = sconf ? sconf->error_log_format : NULL; done = 1; } /* * prepare and log one line */ if (log_format && !info.startup) { len += do_errorlog_format(log_format, &info, errstr + len, MAX_STRING_LEN - len, &errstr_start, &errstr_end, fmt, args); } else { len += do_errorlog_default(&info, errstr + len, MAX_STRING_LEN - len, &errstr_start, &errstr_end, fmt, args); } if (!*errstr) { /* * Don't log empty lines. This can happen with once-per-conn/req * info if an item with AP_ERRORLOG_FLAG_REQUIRED is NULL. */ continue; } write_logline(errstr, len, logf, level_and_mask); if (done) { /* * We don't call the error_log hook for per-request/per-conn * lines, and we only pass the actual log message, not the * prefix and suffix. */ errstr[errstr_end] = '\0'; ap_run_error_log(&info, errstr + errstr_start); } *errstr = '\0'; } }
do_errorlog_default函数拼接要写入日志的内容并暂存在buf中,并返回相应长度len,此处拼接出的日志的内容如下:
[Sat Jun 20 22:08:19.506426 2015] [so:debug] [pid 3826] mod_so.c(266): AH01575: loaded module unixd_module from /app/HRP/lib/mod_unixd.so
do_errorlog_default函数实现在文件:httpd/server/log.c,代码如下:
static int do_errorlog_default(const ap_errorlog_info *info, char *buf, int buflen, int *errstr_start, int *errstr_end, const char *errstr_fmt, va_list args) { int len = 0; int field_start = 0; int item_len; #ifndef AP_UNSAFE_ERROR_LOG_UNESCAPED char scratch[MAX_STRING_LEN]; #endif if (!info->using_syslog && !info->startup) { buf[len++] = '['; len += log_ctime(info, "u", buf + len, buflen - len); buf[len++] = ']'; buf[len++] = ' '; } if (!info->startup) { buf[len++] = '['; len += log_module_name(info, NULL, buf + len, buflen - len); buf[len++] = ':'; len += log_loglevel(info, NULL, buf + len, buflen - len); len += cpystrn(buf + len, "] [pid ", buflen - len); len += log_pid(info, NULL, buf + len, buflen - len); #if APR_HAS_THREADS field_start = len; len += cpystrn(buf + len, ":tid ", buflen - len); item_len = log_tid(info, NULL, buf + len, buflen - len); if (!item_len) len = field_start; else len += item_len; #endif buf[len++] = ']'; buf[len++] = ' '; } if (info->level >= APLOG_DEBUG) { item_len = log_file_line(info, NULL, buf + len, buflen - len); if (item_len) { len += item_len; len += cpystrn(buf + len, ": ", buflen - len); } } if (info->status) { item_len = log_apr_status(info, NULL, buf + len, buflen - len); if (item_len) { len += item_len; len += cpystrn(buf + len, ": ", buflen - len); } } /* * useragent_ip/client_ip can be client or backend server. If we have * a scoreboard handle, it is likely a client. */ if (info->r) { len += apr_snprintf(buf + len, buflen - len, info->r->connection->sbh ? "[client %s:%d] " : "[remote %s:%d] ", info->r->useragent_ip, info->r->useragent_addr ? info->r->useragent_addr->port : 0); } else if (info->c) { len += apr_snprintf(buf + len, buflen - len, info->c->sbh ? "[client %s:%d] " : "[remote %s:%d] ", info->c->client_ip, info->c->client_addr ? info->c->client_addr->port : 0); } /* the actual error message */ *errstr_start = len; #ifndef AP_UNSAFE_ERROR_LOG_UNESCAPED if (apr_vsnprintf(scratch, MAX_STRING_LEN, errstr_fmt, args)) { len += ap_escape_errorlog_item(buf + len, scratch, buflen - len); } #else len += apr_vsnprintf(buf + len, buflen - len, errstr_fmt, args); #endif *errstr_end = len; field_start = len; len += cpystrn(buf + len, ", referer: ", buflen - len); item_len = log_header(info, "Referer", buf + len, buflen - len); if (item_len) len += item_len; else len = field_start; return len; }
write_logline函数实现在文件:httpd/server/log.c,代码如下:
static void write_logline(char *errstr, apr_size_t len, apr_file_t *logf, int level) { /* NULL if we are logging to syslog */ if (logf) { /* Truncate for the terminator (as apr_snprintf does) */ if (len > MAX_STRING_LEN - sizeof(APR_EOL_STR)) { len = MAX_STRING_LEN - sizeof(APR_EOL_STR); } strcpy(errstr + len, APR_EOL_STR); apr_file_puts(errstr, logf); apr_file_flush(logf); } #ifdef HAVE_SYSLOG else { syslog(level < LOG_PRIMASK ? level : APLOG_DEBUG, "%.*s", (int)len, errstr); } #endif }