lighttpd的日志输出很简单,只有两个文件(因此代码没有注明行号)。
一、log.h头文件分析
摘录log.h文件代码如下
代码1
- #ifndef _LOG_H_
- #define _LOG_H_
- #include "server.h"
- #define WP() log_error_write(srv, __FILE__, __LINE__, "");
- int log_error_open(server *srv);
- int log_error_close(server *srv);
- int log_error_write(server *srv, const char *filename, unsigned int line, const char *fmt, ...);
- int log_error_cycle(server *srv);
- #endif
此头文件定义了日志输出所用到的函数,可以看出,真正的输出函数只有一个,就是log_error_write,而且不区分日志的级别。
宏定义WP,只是预先定义了文件名和行号,可变参数直接只有一个,就是""(空字符串),以方便输出。
二、log.c源文件。
我们重点看
int log_error_write(server *srv, const char *filename, unsigned int line, const char *fmt, ...) 函数
其他函数都是一般的普通的日志文件的写法。(创建或追加文件,关闭文件等)
log_error_write函数代码摘录如下:
代码2
- int log_error_write(server *srv, const char *filename, unsigned int line, const char *fmt, ...) {
- va_list ap;
- switch(srv->errorlog_mode) {
- case ERRORLOG_FILE:
- case ERRORLOG_STDERR:
- /* cache the generated timestamp */
- if (srv->cur_ts != srv->last_generated_debug_ts) {
- buffer_prepare_copy(srv->ts_debug_str, 255);
- strftime(srv->ts_debug_str->ptr, srv->ts_debug_str->size - 1, "%Y-%m-%d %H:%M:%S", localtime(&(srv->cur_ts)));
- srv->ts_debug_str->used = strlen(srv->ts_debug_str->ptr) + 1;
- srv->last_generated_debug_ts = srv->cur_ts;
- }
- buffer_copy_string_buffer(srv->errorlog_buf, srv->ts_debug_str);
- BUFFER_APPEND_STRING_CONST(srv->errorlog_buf, ": (");
- break;
- case ERRORLOG_SYSLOG:
- /* syslog is generating its own timestamps */
- BUFFER_COPY_STRING_CONST(srv->errorlog_buf, "(");
- break;
- }
- buffer_append_string(srv->errorlog_buf, filename);
- BUFFER_APPEND_STRING_CONST(srv->errorlog_buf, ".");
- buffer_append_long(srv->errorlog_buf, line);
- BUFFER_APPEND_STRING_CONST(srv->errorlog_buf, ") ");
- for(va_start(ap, fmt); *fmt; fmt++) {
- int d;
- char *s;
- buffer *b;
- off_t o;
- switch(*fmt) {
- case 's': /* string */
- s = va_arg(ap, char *);
- buffer_append_string(srv->errorlog_buf, s);
- BUFFER_APPEND_STRING_CONST(srv->errorlog_buf, " ");
- break;
- case 'b': /* buffer */
- b = va_arg(ap, buffer *);
- buffer_append_string_buffer(srv->errorlog_buf, b);
- BUFFER_APPEND_STRING_CONST(srv->errorlog_buf, " ");
- break;
- case 'd': /* int */
- d = va_arg(ap, int);
- buffer_append_long(srv->errorlog_buf, d);
- BUFFER_APPEND_STRING_CONST(srv->errorlog_buf, " ");
- break;
- case 'o': /* off_t */
- o = va_arg(ap, off_t);
- buffer_append_off_t(srv->errorlog_buf, o);
- BUFFER_APPEND_STRING_CONST(srv->errorlog_buf, " ");
- break;
- case 'x': /* int (hex) */
- d = va_arg(ap, int);
- BUFFER_APPEND_STRING_CONST(srv->errorlog_buf, "0x");
- buffer_append_long_hex(srv->errorlog_buf, d);
- BUFFER_APPEND_STRING_CONST(srv->errorlog_buf, " ");
- break;
- case 'S': /* string */
- s = va_arg(ap, char *);
- buffer_append_string(srv->errorlog_buf, s);
- break;
- case 'B': /* buffer */
- b = va_arg(ap, buffer *);
- buffer_append_string_buffer(srv->errorlog_buf, b);
- break;
- case 'D': /* int */
- d = va_arg(ap, int);
- buffer_append_long(srv->errorlog_buf, d);
- break;
- case '(':
- case ')':
- case '<':
- case '>':
- case ',':
- case ' ':
- buffer_append_string_len(srv->errorlog_buf, fmt, 1);
- break;
- }
- }
- va_end(ap);
- switch(srv->errorlog_mode) {
- case ERRORLOG_FILE:
- BUFFER_APPEND_STRING_CONST(srv->errorlog_buf, "/n");
- write(srv->errorlog_fd, srv->errorlog_buf->ptr, srv->errorlog_buf->used - 1);
- break;
- case ERRORLOG_STDERR:
- BUFFER_APPEND_STRING_CONST(srv->errorlog_buf, "/n");
- write(STDERR_FILENO, srv->errorlog_buf->ptr, srv->errorlog_buf->used - 1);
- break;
- case ERRORLOG_SYSLOG:
- syslog(LOG_ERR, "%s", srv->errorlog_buf->ptr);
- break;
- }
- return 0;
- }
分析此函数代码可以知道,日志输出实际上定义了自己可以解析的格式。
通常的日志输出有两种:
1、自定义格式输出。
2、使用系统的库函数printf支持的格式输出。
两者的比较:
1、定义自己的输出格式。(1)只解析自己定义的错误的格式,这样避免了接口调用者发生系统级的错误(2)调用者要重新学习按此接口提供的格式输出,如果输出格式定义设计要最大程度的满足调用者的需要
2、printf格式输出。(1)很好的兼容,不需要重新遵循新的接口定义。
(2)printf格式基本满足了调用者的最大需求,体现了printf输出格式f的灵活性。
lighttpd的日志输出文件没有自定义了文件名和行号的宏,所以使用的使用要手动加上文件名和行号,比较不方便。