diff --git a/src/common/log.c b/src/common/log.c index f38089a40f..c4a5e683c9 100644 --- a/src/common/log.c +++ b/src/common/log.c @@ -5,7 +5,18 @@ #include "../or/or.h" #include "util.h" -static const char *sev_to_string(int severity) { +struct logfile_t; + +typedef struct logfile_t { + struct logfile_t *next; + const char *filename; + FILE *file; + int needs_close; + int loglevel; + int max_loglevel; +} logfile_t; + +static INLINE const char *sev_to_string(int severity) { switch(severity) { case LOG_DEBUG: return "debug"; case LOG_INFO: return "info"; @@ -20,26 +31,63 @@ static const char *sev_to_string(int severity) { } static int loglevel = LOG_DEBUG; +static logfile_t *logfiles = NULL; + +/* Format a log message into a fixed-sized buffer. (This is factored out + * of 'logv' so that we never format a message more than once. + */ +static INLINE void format_msg(char *buf, size_t buf_len, + int severity, const char *funcname, + const char *format, va_list ap) +{ + time_t t; + struct timeval now; + int n; + + buf_len -= 2; /* subtract 2 characters so we have room for \n\0 */ + + my_gettimeofday(&now); + t = (time_t)now.tv_sec; + + n = strftime(buf, buf_len, "%b %d %H:%M:%S", localtime(&t)); + n += snprintf(buf+n, buf_len-n, + ".%.3ld [%s] ", + (long)now.tv_usec / 1000, sev_to_string(severity)); + if (funcname) + n += snprintf(buf+n, buf_len-n, "%s(): ", funcname); + + n += vsnprintf(buf+n,buf_len-n,format,ap); + buf[n]='\n'; + buf[n+1]='\0'; +} static void logv(int severity, const char *funcname, const char *format, va_list ap) { - char buf[201]; - time_t t; - struct timeval now; + char buf[1024]; + int formatted = 0; + logfile_t *lf; assert(format); if (severity < loglevel) return; - my_gettimeofday(&now); + if (!logfiles) { + /* XXX This is a temporary measure until we get configuration support + for logfiles. */ + add_stream_log(loglevel, "", stdout); + } + for (lf = logfiles; lf; lf = lf->next) { + if (severity < lf->loglevel || severity > lf->max_loglevel) + continue; + if (!lf->file) + continue; - t = time(NULL); - strftime(buf, 200, "%b %d %H:%M:%S", localtime(&t)); - printf("%s.%.3ld [%s] ", buf, (long)now.tv_usec / 1000, sev_to_string(severity)); - if (funcname) - printf("%s(): ", funcname); - vprintf(format,ap); - printf("\n"); + if (!formatted) { + format_msg(buf, 1024, severity, funcname, format, ap); + formatted = 1; + } + fputs(buf, lf->file); + } } void @@ -65,4 +113,47 @@ void _log_fn(int severity, const char *fn, const char *format, ...) va_end(ap); } +void close_logs() +{ + logfile_t *next, *lf; + for (lf = logfiles; lf; lf = lf->next) { + if (lf->needs_close) + fclose(lf->file); + next = lf->next; + free(lf); + } + logfiles = NULL; +} +void reset_logs() +{ + logfile_t *lf; + for (lf = logfiles; lf; lf = lf->next) { + if (lf->needs_close) { + fclose(lf->file); + lf->file = fopen(lf->filename, "a"); + } + } +} + +void add_stream_log(int loglevel, const char *name, FILE *stream) +{ + logfile_t *lf; + lf = tor_malloc(sizeof(logfile_t)); + lf->filename = name; + lf->needs_close = 0; + lf->loglevel = loglevel; + lf->max_loglevel = LOG_EMERG; + lf->file = stream; + lf->next = logfiles; + logfiles = lf; +} + +void add_file_log(int loglevel, const char *filename) +{ + FILE *f; + f = fopen(filename, "a"); + if (!f) return; + add_stream_log(loglevel, filename, f); + logfiles->needs_close = 1; +} diff --git a/src/common/log.h b/src/common/log.h index 38f330c081..9b9d072eec 100644 --- a/src/common/log.h +++ b/src/common/log.h @@ -30,6 +30,11 @@ void log_set_severity(int severity); +void add_stream_log(int loglevel, const char *name, FILE *stream); +void add_file_log(int severity, const char *filename); +void close_logs(); +void reset_logs(); + /* Outputs a message to stdout */ void log(int severity, const char *format, ...) CHECK_PRINTF(2,3);