Message ID | 1339408838-31877-2-git-send-email-colin.king@canonical.com |
---|---|
State | Accepted |
Headers | show |
On 06/11/2012 06:00 AM, Colin King wrote: > From: Colin Ian King <colin.king@canonical.com> > > Since we now can output many different log types we should also > allow for multiple log types to be written during a run. This involves > some considerable re-working of the logging engine. > > 1. The fw->log_type is now a bit map of log_types > 2. We add a list of log types to be written to fwts_log, this is a list > of fwts_log_file types. > 3. We need to re-work the log name handling so that we can open multiple > log files with different suffixes depending on the log type. > 4. To reduce the amount of vsnprintf() of the formatted log output we > now handle this in the log fwts_log_printf() and pass down the formatted > output to the different logging handlers rather than keep on re-formatting > at the lowest logging handler layer. > > There are a lot of changing is this patch. I tried to break it down, but > since there are so many interdependant changes I had to resort to one big > patch > > Signed-off-by: Colin Ian King <colin.king@canonical.com> > --- > src/lib/include/fwts_log.h | 66 +++++-- > src/lib/src/fwts_framework.c | 94 +++++----- > src/lib/src/fwts_log.c | 363 +++++++++++++++++++++++++++++--------- > src/lib/src/fwts_log_html.c | 133 +++++++------- > src/lib/src/fwts_log_json.c | 52 +++--- > src/lib/src/fwts_log_plaintext.c | 63 ++++--- > src/lib/src/fwts_log_xml.c | 72 ++++---- > 7 files changed, 527 insertions(+), 316 deletions(-) > > diff --git a/src/lib/include/fwts_log.h b/src/lib/include/fwts_log.h > index a659baa..513bf88 100644 > --- a/src/lib/include/fwts_log.h > +++ b/src/lib/include/fwts_log.h > @@ -23,7 +23,10 @@ > #include <stdio.h> > #include <stdarg.h> > > -#define LOG_MAGIC 0xfe23ab13 > +#include "fwts_list.h" > + > +#define LOG_MAGIC (0xfe23ab13) > +#define LOG_MAX_BUF_SIZE (4096) /* Max output per log line */ > > typedef enum { > LOG_RESULT = 0x00000001, > @@ -58,31 +61,56 @@ typedef enum { > LOG_LEVEL_INFO = 0x00000010, > } fwts_log_level; > > +/* > + * different types of log file > + */ > typedef enum { > LOG_TYPE_NONE = 0x00000000, > LOG_TYPE_PLAINTEXT = 0x00000001, > LOG_TYPE_JSON = 0x00000002, > - LOG_TYPE_XML = 0x00000003, > - LOG_TYPE_HTML = 0x00000004, > + LOG_TYPE_XML = 0x00000004, > + LOG_TYPE_HTML = 0x00000008, > } fwts_log_type; > > +/* > + * different types of output log > + */ > +typedef enum { > + LOG_FILENAME_TYPE_STDOUT = 0x00000001, /* log output to stdout */ > + LOG_FILENAME_TYPE_STDERR = 0x00000002, /* log output to stderr */ > + LOG_FILENAME_TYPE_FILE = 0x00000003, /* log output to a file */ > +} fwts_log_filename_type; > + > +/* > + * top level log descriptor > + */ > typedef struct log_t { > - unsigned int magic; > - FILE *fp; > - char *owner; > - int line_width; > - int line_number; > - struct fwts_log_ops_t *ops; > + unsigned int magic; /* magic ID of the log */ > + fwts_list log_files; /* list of fwts_log_file */ > + int line_number; /* keeps track of the line numbering */ > + char *owner; /* who is writing to this log */ > } fwts_log; > > +/* > + * info for a specific log type > + */ > +typedef struct { > + FILE *fp; /* file descriptor for log */ > + fwts_log *log; /* parent log struct */ > + fwts_log_type type; /* log type */ > + fwts_log_filename_type filename_type; /* log filename type */ > + struct fwts_log_ops_t *ops; /* log operators */ > + int line_width; /* width of log in chars */ > +} fwts_log_file; > + > typedef struct fwts_log_ops_t { > - int (*vprintf)(fwts_log *log, const fwts_log_field field, const fwts_log_level level, const char *status, const char *label, const char *prefix, const char *fmt, va_list args); > - void (*underline)(fwts_log *log, int ch); > - void (*newline)(fwts_log *log); > - void (*section_begin)(fwts_log *, const char *tag); > - void (*section_end)(fwts_log *); > - void (*open)(fwts_log *); > - void (*close)(fwts_log *); > + int (*print)(fwts_log_file *log_file, const fwts_log_field field, const fwts_log_level level, const char *status, const char *label, const char *prefix, const char *buffer); > + void (*underline)(fwts_log_file *log_file, int ch); > + void (*newline)(fwts_log_file *log_file); > + void (*section_begin)(fwts_log_file *log_file, const char *tag); > + void (*section_end)(fwts_log_file *log_file); > + void (*open)(fwts_log_file *log_file); > + void (*close)(fwts_log_file *log_file); > } fwts_log_ops; > > extern fwts_log_ops fwts_log_plaintext_ops; > @@ -116,6 +144,12 @@ int fwts_log_line_number(fwts_log *log); > void fwts_log_set_line_width(const int width); > void fwts_log_section_begin(fwts_log *log, const char *name); > void fwts_log_section_end(fwts_log *log); > +fwts_log_filename_type fwts_log_get_filename_type(const char *name); > + > +static inline int fwts_log_type_count(fwts_log_type type) > +{ > + return __builtin_popcount(type); > +} > > #define fwts_log_result(fw, fmt, args...) \ > fwts_log_printf(fw->results, LOG_RESULT, LOG_LEVEL_NONE, "", "", "", fmt, ## args) > diff --git a/src/lib/src/fwts_framework.c b/src/lib/src/fwts_framework.c > index f808d8e..a6c2ba2 100644 > --- a/src/lib/src/fwts_framework.c > +++ b/src/lib/src/fwts_framework.c > @@ -28,7 +28,8 @@ > > #include "fwts.h" > > -#define RESULTS_LOG "results.log" > +/* Suffix ".log", ".xml", etc gets automatically appended */ > +#define RESULTS_LOG "results" > > #define FWTS_RUN_ALL_FLAGS \ > (FWTS_BATCH | \ > @@ -96,34 +97,6 @@ static const char *fwts_copyright[] = { > }; > > /* > - * fwts_framework_log_suffix() > - * set the log name suffix > - */ > -static void fwts_framework_log_suffix(fwts_framework *fw, const char *suffix) > -{ > - char *ptr; > - char *new; > - size_t len; > - > - /* Locate old suffix and kill it */ > - ptr = rindex(fw->results_logname, '.'); > - if (ptr != NULL) > - *ptr = '\0'; > - > - /* Space for old log name sans old suffix + new suffix + '.' + '\0' */ > - len = strlen(fw->results_logname) + strlen(suffix) + 2; > - > - if ((new = calloc(len, 1)) == NULL) { > - fprintf(stderr, "Cannot allocate log name.\n"); > - exit(EXIT_FAILURE); > - } > - > - snprintf(new, len, "%s.%s", fw->results_logname, suffix); > - free(fw->results_logname); > - fw->results_logname = new; > -} > - > -/* > * fwts_framework_compare_priority() > * used to register tests sorted on run priority > */ > @@ -677,7 +650,7 @@ static fwts_framework_test *fwts_framework_test_find(fwts_framework *fw, const c > * fwts_framework_log() > * log a test result > */ > -void fwts_framework_log(fwts_framework *fw, > +void fwts_framework_log(fwts_framework *fw, > fwts_log_field field, > const char *label, > fwts_log_level level, > @@ -860,6 +833,39 @@ static int fwts_framework_skip_test_parse(fwts_framework *fw, const char *arg, f > return FWTS_OK; > } > > +/* > + * fwts_framework_log_type_parse() > + * parse optarg of comma separated log types > + */ > +static int fwts_framework_log_type_parse(fwts_framework *fw, const char *arg) > +{ > + char *str; > + char *token; > + char *saveptr = NULL; > + > + fw->log_type = 0; > + > + for (str = (char*)arg; (token = strtok_r(str, ",", &saveptr)) != NULL; str = NULL) { > + if (!strcmp(token, "plaintext")) > + fw->log_type |= LOG_TYPE_PLAINTEXT; > + else if (!strcmp(token, "json")) > + fw->log_type |= LOG_TYPE_JSON; > + else if (!strcmp(token, "xml")) > + fw->log_type |= LOG_TYPE_XML; > + else if (!strcmp(token, "html")) > + fw->log_type |= LOG_TYPE_HTML; > + else { > + fprintf(stderr, "--log-type can be plaintext, xml, html or json.\n"); > + return FWTS_ERROR; > + } > + } > + > + if (!fw->log_type) > + fw->log_type = LOG_TYPE_PLAINTEXT; > + > + return FWTS_OK; > +} > + > int fwts_framework_options_handler(fwts_framework *fw, int argc, char * const argv[], int option_char, int long_index) > { > switch (option_char) { > @@ -975,22 +981,8 @@ int fwts_framework_options_handler(fwts_framework *fw, int argc, char * const ar > fwts_iasl_disassemble_all_to_file(fw); > return FWTS_COMPLETE; > case 32: /* --log-type */ > - if (!strcmp(optarg, "plaintext")) { > - fw->log_type = LOG_TYPE_PLAINTEXT; > - fwts_framework_log_suffix(fw, "log"); > - } else if (!strcmp(optarg, "json")) { > - fw->log_type = LOG_TYPE_JSON; > - fwts_framework_log_suffix(fw, "json"); > - } else if (!strcmp(optarg, "xml")) { > - fw->log_type = LOG_TYPE_XML; > - fwts_framework_log_suffix(fw, "xml"); > - } else if (!strcmp(optarg, "html")) { > - fw->log_type = LOG_TYPE_HTML; > - fwts_framework_log_suffix(fw, "html"); > - } else { > - fprintf(stderr, "--log-type can be either plaintext, xml, html or json.\n"); > - return FWTS_ERROR; > - } > + fwts_framework_log_type_parse(fw, optarg); > + /* FIX ME - check return */ > break; > } > break; > @@ -1136,6 +1128,16 @@ int fwts_framework_args(const int argc, char **argv) > goto tidy_close; > } > > + /* Ensure we have just one log type specified for non-filename logging */ > + if (fwts_log_type_count(fw->log_type) > 1 && > + fwts_log_get_filename_type(fw->results_logname) != LOG_FILENAME_TYPE_FILE) { > + fprintf(stderr, > + "Cannot specify more than one log type when " > + "logging to stderr or stdout\n"); > + ret = FWTS_ERROR; > + goto tidy_close; > + } > + > /* Results log */ > if ((fw->results = fwts_log_open("fwts", > fw->results_logname, > diff --git a/src/lib/src/fwts_log.c b/src/lib/src/fwts_log.c > index fea7c41..3ab7930 100644 > --- a/src/lib/src/fwts_log.c > +++ b/src/lib/src/fwts_log.c > @@ -315,48 +315,125 @@ void fwts_log_set_format(const char *str) > } > > /* > - * fwts_log_vprintf() > - * printf to a log > + * fwts_log_type_filename_suffix() > + * return a filename suffix on a given log type > */ > -int fwts_log_printf(fwts_log *log, > - const fwts_log_field field, > - const fwts_log_level level, > - const char *status, > - const char *label, > - const char *prefix, > - const char *fmt, ...) > +static char *fwts_log_type_filename_suffix(fwts_log_type type) > { > - va_list args; > - int ret; > + switch (type) { > + case LOG_TYPE_JSON: > + return ".json"; > + case LOG_TYPE_XML: > + return ".xml"; > + case LOG_TYPE_HTML: > + return ".html"; > + case LOG_TYPE_NONE: > + case LOG_TYPE_PLAINTEXT: > + default: > + return ".log"; > + } > +} > + > +/* > + * fwts_log_filename_new_suffix() > + * return the log name with suffix based on log type > + */ > +static char *fwts_log_filename(const char *filename, fwts_log_type type) > +{ > + char *ptr; > + char *new_name; > + char *suffix; > + size_t suffix_len; > + size_t trunc_len; > + size_t filename_len; > + > + suffix = fwts_log_type_filename_suffix(type); > + suffix_len = strlen(suffix); > + > + /* > + * Locate an existing suffix, if it is one we recognise > + * then remove it and append the appropriate one > + */ > + ptr = rindex(filename, '.'); > + if (ptr && > + (!strcmp(ptr, ".log") || > + !strcmp(ptr, ".json") || > + !strcmp(ptr, ".xml") || > + !strcmp(ptr, ".html"))) { > + > + trunc_len = ptr - filename; > + if ((new_name = calloc(trunc_len + suffix_len + 1, 1)) == NULL) { > + fprintf(stderr, "Cannot allocate log name.\n"); > + return NULL; > + } > + strncpy(new_name, filename, trunc_len); > + strcat(new_name, suffix); /* strcat OK because calloc zero'd all of new_name */ > + return new_name; > + } > > - va_start(args, fmt); > - ret = fwts_log_vprintf(log, field, level, status, label, prefix, fmt, args); > - va_end(args); > + /* > + * We didn't find a suffix or a known suffix, so append > + * the appropriate one to the given log filename > + */ > + filename_len = strlen(filename); > + if ((new_name = calloc(filename_len + suffix_len + 1, 1)) == NULL) { > + fprintf(stderr, "Cannot allocate log name.\n"); > + return NULL; > + } > > - return ret; > + strcpy(new_name, filename); > + strcat(new_name, suffix); > + > + return new_name; > } > > /* > * fwts_log_vprintf() > - * vprintf to a log > + * printf to a log > */ > -int fwts_log_vprintf(fwts_log *log, > +int fwts_log_printf(fwts_log *log, > const fwts_log_field field, > const fwts_log_level level, > const char *status, > const char *label, > const char *prefix, > - const char *fmt, > - va_list args) > + const char *fmt, ...) > { > + va_list args; > + int ret = 0; > + > + char buffer[LOG_MAX_BUF_SIZE]; > + > if (!((field & LOG_FIELD_MASK) & fwts_log_filter)) > - return 0; > + return ret; > + > + if (log && log->magic == LOG_MAGIC) { > + fwts_list_link *item; > + > + /* > + * With the possibility of having multiple logs being written > + * to per call of fwts_log_printf() it is more efficient to > + * vsnprintf() here and then pass the formatted output down to > + * each log handler rather than re-formatting each time in each > + * handler > + */ > + va_start(args, fmt); > + ret = vsnprintf(buffer, sizeof(buffer), fmt, args); > + if (ret < 0) > + return ret; > + > + fwts_list_foreach(item, &log->log_files) { > + fwts_log_file *log_file = fwts_list_data(fwts_log_file *, item); > + > + if (log_file->ops && log_file->ops->print) > + log_file->ops->print(log_file, field, level, > + status, label, prefix, buffer); > + } > + log->line_number++; > > - if (log && log->magic == LOG_MAGIC && > - log->ops && log->ops->underline) > - return log->ops->vprintf(log, field, level, status, label, prefix, fmt, args); > - else > - return 0; > + va_end(args); > + } > + return ret; > } > > /* > @@ -365,9 +442,16 @@ int fwts_log_vprintf(fwts_log *log, > */ > void fwts_log_underline(fwts_log *log, const int ch) > { > - if (log && log->magic == LOG_MAGIC && > - log->ops && log->ops->underline) > - log->ops->underline(log, ch); > + if (log && log->magic == LOG_MAGIC) { > + fwts_list_link *item; > + > + fwts_list_foreach(item, &log->log_files) { > + fwts_log_file *log_file = fwts_list_data(fwts_log_file *, item); > + > + if (log_file->ops && log_file->ops->underline) > + log_file->ops->underline(log_file, ch); > + } > + } > } > > /* > @@ -376,9 +460,17 @@ void fwts_log_underline(fwts_log *log, const int ch) > */ > void fwts_log_newline(fwts_log *log) > { > - if (log && log->magic == LOG_MAGIC && > - log->ops && log->ops->underline) > - log->ops->newline(log); > + if (log && log->magic == LOG_MAGIC) { > + fwts_list_link *item; > + > + fwts_list_foreach(item, &log->log_files) { > + fwts_log_file *log_file = fwts_list_data(fwts_log_file *, item); > + > + if (log_file->ops && log_file->ops->newline) > + log_file->ops->newline(log_file); > + } > + log->line_number++; > + } > } > > int fwts_log_set_owner(fwts_log *log, const char *owner) > @@ -395,96 +487,199 @@ int fwts_log_set_owner(fwts_log *log, const char *owner) > return FWTS_ERROR; > } > > + > +/* > + * fwts_log_section_begin() > + * mark a start of a named section. For structured logging > + * such as XML and JSON this pushes a new named tagged section > + */ > void fwts_log_section_begin(fwts_log *log, const char *name) > { > - if (log && log->magic == LOG_MAGIC && > - log->ops && log->ops->section_begin) > - log->ops->section_begin(log, name); > + if (log && log->magic == LOG_MAGIC) { > + fwts_list_link *item; > + > + fwts_list_foreach(item, &log->log_files) { > + fwts_log_file *log_file = fwts_list_data(fwts_log_file *, item); > + > + if (log_file->ops && log_file->ops->section_begin) > + log_file->ops->section_begin(log_file, name); > + } > + } > } > > +/* > + * fwts_log_section_end() > + * mark end of a named section. For structured logging > + * such as XML and JSON this pops the end of a tagged section > + */ > void fwts_log_section_end(fwts_log *log) > { > - if (log && log->magic == LOG_MAGIC && > - log->ops && log->ops->section_end) > - log->ops->section_end(log); > + if (log && log->magic == LOG_MAGIC) { > + fwts_list_link *item; > + > + fwts_list_foreach(item, &log->log_files) { > + fwts_log_file *log_file = fwts_list_data(fwts_log_file *, item); > + > + if (log_file->ops && log_file->ops->section_end) > + log_file->ops->section_end(log_file); > + } > + } > } > > /* > - * fwts_log_open() > - * open a log file. if name is stderr or stdout, then attach log to these > - * streams. > + * fwts_log_get_ops() > + * return log ops basedon log type > */ > -fwts_log *fwts_log_open(const char *owner, const char *name, const char *mode, fwts_log_type type) > +static fwts_log_ops *fwts_log_get_ops(fwts_log_type type) > { > - fwts_log *newlog; > - > - if ((newlog = calloc(1, sizeof(fwts_log))) == NULL) > - return NULL; > - > - newlog->magic = LOG_MAGIC; > switch (type) { > case LOG_TYPE_JSON: > - newlog->ops = &fwts_log_json_ops; > - break; > + return &fwts_log_json_ops; > case LOG_TYPE_PLAINTEXT: > - newlog->ops = &fwts_log_plaintext_ops; > - break; > + return &fwts_log_plaintext_ops; > case LOG_TYPE_XML: > - newlog->ops = &fwts_log_xml_ops; > - break; > + return &fwts_log_xml_ops; > case LOG_TYPE_HTML: > - newlog->ops = &fwts_log_html_ops; > - break; > + return &fwts_log_html_ops; > case LOG_TYPE_NONE: > default: > - newlog->ops = &fwts_log_plaintext_ops; > - break; > + return &fwts_log_plaintext_ops; > } > +} > > - if (owner) { > - if ((newlog->owner = calloc(1, strlen(owner)+1)) == NULL) { > - free(newlog); > - return NULL; > - } > - strcpy(newlog->owner, owner); > - } > +/* > + * fwts_log_get_filename_type() > + * determine the filename type > + */ > +fwts_log_filename_type fwts_log_get_filename_type(const char *filename) > +{ > + if (!strcmp(filename, "stderr")) > + return LOG_FILENAME_TYPE_STDERR; > + else if (!strcmp(filename, "stdout")) > + return LOG_FILENAME_TYPE_STDOUT; > + else > + return LOG_FILENAME_TYPE_FILE; > +} > + > +/* > + * fwts_log_open() > + * open a log file. if name is stderr or stdout, then attach log to these > + * streams. > + */ > +fwts_log *fwts_log_open( > + const char *owner, /* Creator of the log */ > + const char *filename, /* Log file name */ > + const char *mode, /* open mode, see fopen() modes */ > + fwts_log_type type) /* Log type */ > +{ > + fwts_log *newlog; > + unsigned int i; > + char *newname; > > - if (strcmp("stderr", name) == 0) > - newlog->fp = stderr; > - else if (strcmp("stdout", name) == 0) > - newlog->fp = stdout; > - else if ((newlog->fp = fopen(name, mode)) == NULL) { > - free(newlog); > + if ((newlog = calloc(1, sizeof(fwts_log))) == NULL) > return NULL; > - } > > - if (log_line_width) { > - /* User has specified width, so use it */ > - newlog->line_width = log_line_width; > - } else { > - newlog->line_width = fwts_tty_width(fileno(newlog->fp), LOG_LINE_WIDTH); > - } > + newlog->magic = LOG_MAGIC; > > - if (newlog->ops && newlog->ops->open) > - newlog->ops->open(newlog); > + fwts_log_set_owner(newlog, owner); > + fwts_list_init(&newlog->log_files); > + > + /* > + * Scan through and see which log types have been specified > + * and open the log file with the appropriate ops to perform > + * the logging > + */ > + for (i=0; i<32; i++) { > + fwts_log_type mask = 1 << i; /* The log type for this iteration */ > + > + /* If set then go and open up a log for this log type */ > + if (type & mask) { > + fwts_log_file *log_file; > + > + if ((log_file = calloc(1, sizeof(fwts_log_file))) == NULL) { > + fwts_log_close(newlog); > + return NULL; > + } > + > + log_file->type = mask; > + log_file->ops = fwts_log_get_ops(mask); > + log_file->log = newlog; > + log_file->filename_type = fwts_log_get_filename_type(filename); > + > + /* > + * To complicate matters we can have logs being > + * written to stderr, stdout or two a named file > + */ > + switch(log_file->filename_type) { > + case LOG_FILENAME_TYPE_STDERR: > + log_file->fp = stderr; > + break; > + case LOG_FILENAME_TYPE_STDOUT: > + log_file->fp = stdout; > + break; > + case LOG_FILENAME_TYPE_FILE: > + if ((newname = fwts_log_filename(filename, mask)) == NULL) { > + fwts_log_close(newlog); > + return NULL; > + } > + log_file->fp = fopen(newname, mode); > + free(newname); > + > + if (log_file->fp == NULL) { > + fwts_log_close(newlog); > + return NULL; > + } > + } > + > + /* Fix up the log specific line width */ > + if (log_line_width) { > + /* User has specified width, so use it */ > + log_file->line_width = log_line_width; > + } else { > + log_file->line_width = > + fwts_tty_width(fileno(log_file->fp), LOG_LINE_WIDTH); > + } > + > + /* ..and add the log file to the list of logs */ > + fwts_list_append(&newlog->log_files, log_file); > + > + /* ..and do the log specific opening set up */ > + if (log_file->ops && log_file->ops->open) > + log_file->ops->open(log_file); > + } > + } > > return newlog; > } > > /* > * fwts_log_close() > - * close a log file > + * close any opened log files, free up memory > */ > int fwts_log_close(fwts_log *log) > { > if (log && (log->magic == LOG_MAGIC)) { > - if (log->ops && log->ops->close) > - log->ops->close(log); > + fwts_list_link *item; > + > + fwts_list_foreach(item, &log->log_files) { > + fwts_log_file *log_file = fwts_list_data(fwts_log_file *, item); > + > + /* Do the log type specific close */ > + if (log_file->ops && log_file->ops->close) > + log_file->ops->close(log_file); > + > + /* Close opened log file */ > + if (log_file->fp && > + log_file->filename_type == LOG_FILENAME_TYPE_FILE) > + fclose(log_file->fp); > + } > + > + /* ..and free log files */ > + fwts_list_free_items(&log->log_files, free); > > - if (log->fp && (log->fp != stdout && log->fp != stderr)) > - fclose(log->fp); > if (log->owner) > free(log->owner); > + > free(log); > } > return FWTS_OK; > diff --git a/src/lib/src/fwts_log_html.c b/src/lib/src/fwts_log_html.c > index a70ac21..1ca79b8 100644 > --- a/src/lib/src/fwts_log_html.c > +++ b/src/lib/src/fwts_log_html.c > @@ -37,31 +37,31 @@ typedef struct { > static fwts_log_html_stack_t html_stack[MAX_HTML_STACK]; > static int html_stack_index = 0; > > -static void fwts_log_html(fwts_log *log, const char *fmt, ...) > +static void fwts_log_html(fwts_log_file *log_file, const char *fmt, ...) > { > va_list args; > > va_start(args, fmt); > > - fprintf(log->fp, "%*s", html_stack_index * HTML_INDENT, ""); > - vfprintf(log->fp, fmt, args); > + fprintf(log_file->fp, "%*s", html_stack_index * HTML_INDENT, ""); > + vfprintf(log_file->fp, fmt, args); > > va_end(args); > } > > > /* > - * fwts_log_vprintf_html() > - * vprintf to a log > + * fwts_log_print_html() > + * print to a log > */ > -static int fwts_log_vprintf_html(fwts_log *log, > +static int fwts_log_print_html( > + fwts_log_file *log_file, > const fwts_log_field field, > const fwts_log_level level, > const char *status, > const char *label, > const char *prefix, > - const char *fmt, > - va_list args) > + const char *buffer) > { > char *str; > char *style; > @@ -74,7 +74,7 @@ static int fwts_log_vprintf_html(fwts_log *log, > if (field & (LOG_NEWLINE | LOG_SEPARATOR | LOG_DEBUG)) > return 0; > > - fwts_log_html(log, "<TR>\n"); > + fwts_log_html(log_file, "<TR>\n"); > > if (field & LOG_VERBATUM) { > code_start = "<PRE class=style_code>"; > @@ -86,29 +86,24 @@ static int fwts_log_vprintf_html(fwts_log *log, > > switch (field & LOG_FIELD_MASK) { > case LOG_ERROR: > - fwts_log_html(log, " <TD class=style_error>Error</TD><TD COLSPAN=2>"); > - vfprintf(log->fp, fmt, args); > - fprintf(log->fp, "</TD>\n"); > + fwts_log_html(log_file, " <TD class=style_error>Error</TD>" > + "<TD COLSPAN=2>%s</TD>\n", buffer); > break; > case LOG_WARNING: > - fwts_log_html(log, " <TD class=style_error>Warning</TD><TD COLSPAN=2 class=style_advice_info>%s", code_start); > - vfprintf(log->fp, fmt, args); > - fprintf(log->fp, "%s</TD>\n", code_end); > + fwts_log_html(log_file, " <TD class=style_error>Warning</TD>" > + "<TD COLSPAN=2 class=style_advice_info>%s%s%s</TD>\n", > + code_start, buffer, code_end); > break; > case LOG_HEADING: > - fwts_log_html(log, "<TD COLSPAN=2 class=style_heading>%s", code_start); > - vfprintf(log->fp, fmt, args); > - fprintf(log->fp, "%s</TD>\n", code_end); > + fwts_log_html(log_file, "<TD COLSPAN=2 class=style_heading>%s%s%s</TD>\n", > + code_start, buffer, code_end); > break; > case LOG_INFO: > - fwts_log_html(log, " <TD></TD><TD COLSPAN=2 class=style_infos>%s", code_start); > - vfprintf(log->fp, fmt, args); > - fprintf(log->fp, "%s</TD>\n", code_end); > + fwts_log_html(log_file, " <TD></TD><TD COLSPAN=2 class=style_infos>%s%s%s</TD>\n", > + code_start, buffer, code_end); > break; > case LOG_PASSED: > - fwts_log_html(log, "<TD class=style_passed>PASSED</TD><TD>"); > - vfprintf(log->fp, fmt, args); > - fprintf(log->fp, "</TD>\n"); > + fwts_log_html(log_file, "<TD class=style_passed>PASSED</TD><TD>%s</TD>\n", buffer); > break; > case LOG_FAILED: > switch (level) { > @@ -132,39 +127,33 @@ static int fwts_log_vprintf_html(fwts_log *log, > } > str = fwts_log_level_to_str(level); > > - fwts_log_html(log, " <TD%s>%s [%s]</TD>\n", style, *status ? status : "", str); > - > - fwts_log_html(log, " <TD>"); > - vfprintf(log->fp, fmt, args); > - fprintf(log->fp, "</TD>\n"); > + fwts_log_html(log_file, " <TD%s>%s [%s]</TD>\n", style, *status ? status : "", str); > + fwts_log_html(log_file, " <TD>%s</TD>\n", buffer); > break; > > case LOG_SKIPPED: > - fwts_log_html(log, "<TD class=style_skipped>Skipped</TD><TD>%s", code_start); > - vfprintf(log->fp, fmt, args); > - fprintf(log->fp, "%s</TD>\n", code_end); > + fwts_log_html(log_file, "<TD class=style_skipped>Skipped</TD>" > + "<TD>%s%s%s</TD>\n", code_start, buffer, code_end); > break; > > case LOG_SUMMARY: > - fwts_log_html(log, " <TD></TD><TD COLSPAN=2 class=style_summary>%s", code_start); > - vfprintf(log->fp, fmt, args); > - fprintf(log->fp, "%s</TD>\n", code_end); > + fwts_log_html(log_file, " <TD></TD>" > + "<TD COLSPAN=2 class=style_summary>%s%s%s</TD>\n", > + code_start, buffer, code_end); > break; > > case LOG_ADVICE: > - fwts_log_html(log, " <TD class=style_advice>Advice</TD><TD COLSPAN=2 class=style_advice_info>%s", code_start); > - vfprintf(log->fp, fmt, args); > - fprintf(log->fp, "%s</TD>\n", code_end); > + fwts_log_html(log_file, " <TD class=style_advice>Advice</TD>" > + "<TD COLSPAN=2 class=style_advice_info>%s%s%s</TD>\n", > + code_start, buffer, code_end); > break; > > default: > break; > } > > - fwts_log_html(log, "</TR>\n"); > - fflush(log->fp); > - > - log->line_number++; > + fwts_log_html(log_file, "</TR>\n"); > + fflush(log_file->fp); > > return 0; > } > @@ -173,7 +162,7 @@ static int fwts_log_vprintf_html(fwts_log *log, > * fwts_log_underline_html() > * write an underline across log, using character ch as the underline > */ > -static void fwts_log_underline_html(fwts_log *log, const int ch) > +static void fwts_log_underline_html(fwts_log_file *log_file, const int ch) > { > /* No-op for html */ > } > @@ -182,26 +171,26 @@ static void fwts_log_underline_html(fwts_log *log, const int ch) > * fwts_log_newline() > * write newline to log > */ > -static void fwts_log_newline_html(fwts_log *log) > +static void fwts_log_newline_html(fwts_log_file *log_file) > { > /* No-op for html */ > } > > -static void fwts_log_section_begin_html(fwts_log *log, const char *name) > +static void fwts_log_section_begin_html(fwts_log_file *log_file, const char *name) > { > html_stack[html_stack_index].name = name; > > if (!strcmp(name, "summary")) { > - fwts_log_html(log, "<TR><TD class=style_heading COLSPAN=2>Summary</TD></TR>\n"); > + fwts_log_html(log_file, "<TR><TD class=style_heading COLSPAN=2>Summary</TD></TR>\n"); > } else if (!strcmp(name, "heading")) { > - fwts_log_html(log, "<TR><TD class=style_heading COLSPAN=2>Firmware Test Suite</TD></TR>\n"); > + fwts_log_html(log_file, "<TR><TD class=style_heading COLSPAN=2>Firmware Test Suite</TD></TR>\n"); > } else if (!strcmp(name, "subtest_info")) { > - fwts_log_html(log, "<TR><TD class=style_subtest COLSPAN=2></TD></TR>\n"); > + fwts_log_html(log_file, "<TR><TD class=style_subtest COLSPAN=2></TD></TR>\n"); > } else if (!strcmp(name, "failure")) { > - fwts_log_html(log, "<TR><TD class=style_heading COLSPAN=2></TD></TR>\n"); > + fwts_log_html(log_file, "<TR><TD class=style_heading COLSPAN=2></TD></TR>\n"); > } > > - fflush(log->fp); > + fflush(log_file->fp); > > if (html_stack_index < MAX_HTML_STACK) > html_stack_index++; > @@ -211,11 +200,11 @@ static void fwts_log_section_begin_html(fwts_log *log, const char *name) > } > } > > -static void fwts_log_section_end_html(fwts_log *log) > +static void fwts_log_section_end_html(fwts_log_file *log_file) > { > if (html_stack_index > 0) { > html_stack_index--; > - fflush(log->fp); > + fflush(log_file->fp); > } else { > fprintf(stderr, "html log stack underflow.\n"); > exit(EXIT_FAILURE); > @@ -223,15 +212,15 @@ static void fwts_log_section_end_html(fwts_log *log) > > } > > -static void fwts_log_open_html(fwts_log *log) > +static void fwts_log_open_html(fwts_log_file *log_file) > { > - fwts_log_html(log, "<HTML>\n"); > - fwts_log_html(log, "<HEAD>\n"); > - fwts_log_html(log, " <TITLE>fwts log</TITLE>\n"); > - fwts_log_html(log, "</HEAD>\n"); > - fwts_log_html(log, "<BODY>\n"); > - fwts_log_html(log, "<STYLE>\n"); > - fwts_log_html(log, > + fwts_log_html(log_file, "<HTML>\n"); > + fwts_log_html(log_file, "<HEAD>\n"); > + fwts_log_html(log_file, " <TITLE>fwts log</TITLE>\n"); > + fwts_log_html(log_file, "</HEAD>\n"); > + fwts_log_html(log_file, "<BODY>\n"); > + fwts_log_html(log_file, "<STYLE>\n"); > + fwts_log_html(log_file, > ".style_critical { background-color: red; font-weight: bold; " > "text-align: center; vertical-align: center }\n" > ".style_high { background-color: orange; font-weight: bold; " > @@ -256,27 +245,27 @@ static void fwts_log_open_html(fwts_log *log) > ".style_info { }\n" > ".style_code { font-family: \"courier\",\"mono\"; font-size:0.75em; overflow:auto; " > "width:90%; line-height:0.82em; font-stretch:extra-condensed; word-wrap:normal }\n"); > - fwts_log_html(log, "</STYLE>\n"); > - fflush(log->fp); > + fwts_log_html(log_file, "</STYLE>\n"); > + fflush(log_file->fp); > > - fwts_log_html(log, "<TABLE WIDTH=1024>\n"); > - fwts_log_html(log, "</TR>\n"); > + fwts_log_html(log_file, "<TABLE WIDTH=1024>\n"); > + fwts_log_html(log_file, "</TR>\n"); > > - fwts_log_section_begin_html(log, "fwts"); > + fwts_log_section_begin_html(log_file, "fwts"); > } > > -static void fwts_log_close_html(fwts_log *log) > +static void fwts_log_close_html(fwts_log_file *log_file) > { > - fwts_log_section_end_html(log); > + fwts_log_section_end_html(log_file); > > - fwts_log_html(log, "</TABLE>\n"); > - fwts_log_html(log, "</BODY>\n"); > - fwts_log_html(log, "</HTML>\n"); > - fflush(log->fp); > + fwts_log_html(log_file, "</TABLE>\n"); > + fwts_log_html(log_file, "</BODY>\n"); > + fwts_log_html(log_file, "</HTML>\n"); > + fflush(log_file->fp); > } > > fwts_log_ops fwts_log_html_ops = { > - .vprintf = fwts_log_vprintf_html, > + .print = fwts_log_print_html, > .underline = fwts_log_underline_html, > .newline = fwts_log_newline_html, > .section_begin = fwts_log_section_begin_html, > diff --git a/src/lib/src/fwts_log_json.c b/src/lib/src/fwts_log_json.c > index 208847c..8dd65e4 100644 > --- a/src/lib/src/fwts_log_json.c > +++ b/src/lib/src/fwts_log_json.c > @@ -40,19 +40,19 @@ static fwts_log_json_stack_t json_stack[MAX_JSON_STACK]; > static int json_stack_index = 0; > > /* > - * fwts_log_vprintf_json() > - * vprintf to a log > + * fwts_log_printf_son() > + * print to a log > */ > -static int fwts_log_vprintf_json(fwts_log *log, > +static int fwts_log_print_json( > + fwts_log_file *log_file, > const fwts_log_field field, > const fwts_log_level level, > const char *status, > const char *label, > const char *prefix, > - const char *fmt, > - va_list args) > + const char *buffer) > { > - char buffer[4096]; > + char tmpbuf[4096]; > struct tm tm; > time_t now; > json_object *header; > @@ -69,13 +69,13 @@ static int fwts_log_vprintf_json(fwts_log *log, > localtime_r(&now, &tm); > > header = json_object_new_object(); > - json_object_object_add(header, "line_num", json_object_new_int(log->line_number)); > - snprintf(buffer, sizeof(buffer), "%2.2d/%2.2d/%-2.2d", > + json_object_object_add(header, "line_num", json_object_new_int(log_file->log->line_number)); > + snprintf(tmpbuf, sizeof(tmpbuf), "%2.2d/%2.2d/%-2.2d", > tm.tm_mday, tm.tm_mon + 1, (tm.tm_year+1900) % 100); > - json_object_object_add(header, "date", json_object_new_string(buffer)); > - snprintf(buffer, sizeof(buffer), "%2.2d:%2.2d:%2.2d", > + json_object_object_add(header, "date", json_object_new_string(tmpbuf)); > + snprintf(tmpbuf, sizeof(tmpbuf), "%2.2d:%2.2d:%2.2d", > tm.tm_hour, tm.tm_min, tm.tm_sec); > - json_object_object_add(header, "time", json_object_new_string(buffer)); > + json_object_object_add(header, "time", json_object_new_string(tmpbuf)); > json_object_object_add(header, "field_type", > json_object_new_string(fwts_log_field_to_str_full(field))); > > @@ -92,13 +92,10 @@ static int fwts_log_vprintf_json(fwts_log *log, > json_object_object_add(header, "owner", > json_object_new_string(log->owner)); > */ > - vsnprintf(buffer, sizeof(buffer), fmt, args); > json_object_object_add(header, "log_text", json_object_new_string(buffer)); > > json_object_array_add(json_log, header); > > - log->line_number++; > - > return 0; > } > > @@ -106,7 +103,7 @@ static int fwts_log_vprintf_json(fwts_log *log, > * fwts_log_underline_json() > * write an underline across log, using character ch as the underline > */ > -static void fwts_log_underline_json(fwts_log *log, const int ch) > +static void fwts_log_underline_json(fwts_log_file *log_file, const int ch) > { > /* No-op for json */ > } > @@ -115,12 +112,12 @@ static void fwts_log_underline_json(fwts_log *log, const int ch) > * fwts_log_newline() > * write newline to log > */ > -static void fwts_log_newline_json(fwts_log *log) > +static void fwts_log_newline_json(fwts_log_file *log_file) > { > /* No-op for json */ > } > > -static void fwts_log_section_begin_json(fwts_log *log, const char *name) > +static void fwts_log_section_begin_json(fwts_log_file *log_file, const char *name) > { > json_object *json_obj; > json_object *json_log; > @@ -132,7 +129,7 @@ static void fwts_log_section_begin_json(fwts_log *log, const char *name) > json_stack[json_stack_index].obj = json_obj; > json_stack[json_stack_index].log = json_log; > > - if (json_stack_index > 0) > + if (json_stack_index > 0) > json_object_array_add(json_stack[json_stack_index-1].log, json_obj); > > if (json_stack_index < MAX_JSON_STACK) > @@ -143,7 +140,7 @@ static void fwts_log_section_begin_json(fwts_log *log, const char *name) > } > } > > -static void fwts_log_section_end_json(fwts_log *log) > +static void fwts_log_section_end_json(fwts_log_file *log_file) > { > if (json_stack_index > 0) > json_stack_index--; > @@ -153,29 +150,30 @@ static void fwts_log_section_end_json(fwts_log *log) > } > } > > -static void fwts_log_open_json(fwts_log *log) > +static void fwts_log_open_json(fwts_log_file *log_file) > { > - fwts_log_section_begin_json(log, "fwts"); > + fwts_log_section_begin_json(log_file, "fwts"); > } > > -static void fwts_log_close_json(fwts_log *log) > +static void fwts_log_close_json(fwts_log_file *log_file) > { > const char *str; > size_t len; > > - fwts_log_section_end_json(log); > + fwts_log_section_end_json(log_file); > > str = json_object_to_json_string(json_stack[0].obj); > len = strlen(str); > > - fwrite(str, 1, len, log->fp); > - fwrite("\n", 1, 1, log->fp); > - fflush(log->fp); > + fwrite(str, 1, len, log_file->fp); > + fwrite("\n", 1, 1, log_file->fp); > + fflush(log_file->fp); > + > json_object_put(json_stack[0].obj); > } > > fwts_log_ops fwts_log_json_ops = { > - .vprintf = fwts_log_vprintf_json, > + .print = fwts_log_print_json, > .underline = fwts_log_underline_json, > .newline = fwts_log_newline_json, > .section_begin = fwts_log_section_begin_json, > diff --git a/src/lib/src/fwts_log_plaintext.c b/src/lib/src/fwts_log_plaintext.c > index 44c443f..7381ae3 100644 > --- a/src/lib/src/fwts_log_plaintext.c > +++ b/src/lib/src/fwts_log_plaintext.c > @@ -32,7 +32,8 @@ > * fwts_log_header_plaintext() > * format up a tabulated log heading > */ > -static int fwts_log_header_plaintext(fwts_log *log, > +static int fwts_log_header_plaintext( > + fwts_log_file *log_file, > char *buffer, > const int len, > const fwts_log_field field, > @@ -51,7 +52,7 @@ static int fwts_log_header_plaintext(fwts_log *log, > ptr++; > if (!strncmp(ptr, "line", 4)) { > n += snprintf(buffer + n, len - n, > - "%5.5d", log->line_number); > + "%5.5d", log_file->log->line_number); > ptr += 4; > } > if (!strncmp(ptr, "date", 4)) { > @@ -76,8 +77,8 @@ static int fwts_log_header_plaintext(fwts_log *log, > fwts_log_level_to_str(level)); > ptr += 5; > } > - if (!strncmp(ptr,"owner", 5) && log->owner) { > - n += snprintf(buffer + n, len - n, "%-15.15s", log->owner); > + if (!strncmp(ptr,"owner", 5) && log_file->log->owner) { > + n += snprintf(buffer + n, len - n, "%-15.15s", log_file->log->owner); > ptr += 5; > } > } else { > @@ -90,19 +91,19 @@ static int fwts_log_header_plaintext(fwts_log *log, > > > /* > - * fwts_log_vprintf() > - * vprintf to a log > + * fwts_log_print() > + * print to a log > */ > -static int fwts_log_vprintf_plaintext(fwts_log *log, > +static int fwts_log_print_plaintext( > + fwts_log_file *log_file, > const fwts_log_field field, > const fwts_log_level level, > const char *status, /* Ignored */ > const char *label, /* Ignored */ > const char *prefix, > - const char *fmt, > - va_list args) > + const char *buffer) > { > - char buffer[4096]; > + char tmpbuf[8192]; > int n = 0; > int header_len; > int len = 0; > @@ -115,15 +116,14 @@ static int fwts_log_vprintf_plaintext(fwts_log *log, > > /* This is a pain, we neen to find out how big the leading log > message is, so format one up. */ > - n = header_len = fwts_log_header_plaintext(log, buffer, sizeof(buffer), field, level); > - n += snprintf(buffer + n, sizeof(buffer) - n, "%s", prefix); > - n += vsnprintf(buffer + n, sizeof(buffer) - n, fmt, args); > + n = header_len = fwts_log_header_plaintext(log_file, tmpbuf, sizeof(tmpbuf), field, level); > + n += snprintf(tmpbuf + n, sizeof(tmpbuf) - n, "%s%s", prefix, buffer); > > /* Break text into multi-lines if necessary */ > if (field & LOG_VERBATUM) > - lines = fwts_list_from_text(buffer + header_len); > + lines = fwts_list_from_text(tmpbuf + header_len); > else > - lines = fwts_format_text(buffer + header_len, log->line_width - header_len); > + lines = fwts_format_text(tmpbuf + header_len, log_file->line_width - header_len); > > len = n; > > @@ -133,17 +133,16 @@ static int fwts_log_vprintf_plaintext(fwts_log *log, > if (!(field & LOG_NO_FIELDS)) { > /* Re-format up a log heading with current line number which > may increment with multiple line log messages */ > - fwts_log_header_plaintext(log, buffer, sizeof(buffer), field, level); > - fwrite(buffer, 1, header_len, log->fp); > + fwts_log_header_plaintext(log_file, tmpbuf, sizeof(tmpbuf), field, level); > + fwrite(tmpbuf, 1, header_len, log_file->fp); > } > - fwrite(text, 1, strlen(text), log->fp); > - fwrite("\n", 1, 1, log->fp); > - fflush(log->fp); > - log->line_number++; > + fwrite(text, 1, strlen(text), log_file->fp); > + fwrite("\n", 1, 1, log_file->fp); > + fflush(log_file->fp); > len += strlen(text) + 1; > } > fwts_text_list_free(lines); > - > + > return len; > } > > @@ -151,11 +150,11 @@ static int fwts_log_vprintf_plaintext(fwts_log *log, > * fwts_log_underline_plaintext() > * write an underline across log, using character ch as the underline > */ > -static void fwts_log_underline_plaintext(fwts_log *log, const int ch) > +static void fwts_log_underline_plaintext(fwts_log_file *log_file, const int ch) > { > int n; > char *buffer; > - size_t width = log->line_width + 1; > + size_t width = log_file->line_width + 1; > > if (!((LOG_SEPARATOR & LOG_FIELD_MASK) & fwts_log_filter)) > return; > @@ -165,14 +164,13 @@ static void fwts_log_underline_plaintext(fwts_log *log, const int ch) > return; /* Unlikely, and just abort */ > > /* Write in leading optional line prefix */ > - n = fwts_log_header_plaintext(log, buffer, width, LOG_SEPARATOR, LOG_LEVEL_NONE); > + n = fwts_log_header_plaintext(log_file, buffer, width, LOG_SEPARATOR, LOG_LEVEL_NONE); > > memset(buffer + n, ch, width - n); > buffer[width - 1] = '\n'; > > - fwrite(buffer, 1, width, log->fp); > - fflush(log->fp); > - log->line_number++; > + fwrite(buffer, 1, width, log_file->fp); > + fflush(log_file->fp); > > free(buffer); > } > @@ -181,15 +179,14 @@ static void fwts_log_underline_plaintext(fwts_log *log, const int ch) > * fwts_log_newline_plaintext() > * write newline to log > */ > -static void fwts_log_newline_plaintext(fwts_log *log) > +static void fwts_log_newline_plaintext(fwts_log_file *log_file) > { > - fwrite("\n", 1, 1, log->fp); > - fflush(log->fp); > - log->line_number++; > + fwrite("\n", 1, 1, log_file->fp); > + fflush(log_file->fp); > } > > fwts_log_ops fwts_log_plaintext_ops = { > - .vprintf = fwts_log_vprintf_plaintext, > + .print = fwts_log_print_plaintext, > .underline = fwts_log_underline_plaintext, > .newline = fwts_log_newline_plaintext > }; > diff --git a/src/lib/src/fwts_log_xml.c b/src/lib/src/fwts_log_xml.c > index 57b530b..19e5e94 100644 > --- a/src/lib/src/fwts_log_xml.c > +++ b/src/lib/src/fwts_log_xml.c > @@ -38,19 +38,18 @@ static fwts_log_xml_stack_t xml_stack[MAX_XML_STACK]; > static int xml_stack_index = 0; > > /* > - * fwts_log_vprintf_xml() > - * vprintf to a log > + * fwts_log_print_xml() > + * print to a log > */ > -static int fwts_log_vprintf_xml(fwts_log *log, > +static int fwts_log_print_xml( > + fwts_log_file *log_file, > const fwts_log_field field, > const fwts_log_level level, > const char *status, > const char *label, > const char *prefix, > - const char *fmt, > - va_list args) > + const char *buffer) > { > - char buffer[4096]; > struct tm tm; > time_t now; > char *str; > @@ -64,21 +63,21 @@ static int fwts_log_vprintf_xml(fwts_log *log, > time(&now); > localtime_r(&now, &tm); > > - fprintf(log->fp, "%*s<logentry>\n", xml_stack_index * XML_INDENT, ""); > + fprintf(log_file->fp, "%*s<logentry>\n", xml_stack_index * XML_INDENT, ""); > > - fprintf(log->fp, "%*s<line_num>%d</line_num>\n", > + fprintf(log_file->fp, "%*s<line_num>%d</line_num>\n", > (xml_stack_index + 1) * XML_INDENT, > - "", log->line_number); > + "", log_file->log->line_number); > > - fprintf(log->fp, "%*s<date>%2.2d/%2.2d/%-2.2d</date>\n", > + fprintf(log_file->fp, "%*s<date>%2.2d/%2.2d/%-2.2d</date>\n", > (xml_stack_index + 1) * XML_INDENT, > "", tm.tm_mday, tm.tm_mon + 1, (tm.tm_year+1900) % 100); > > - fprintf(log->fp, "%*s<time>%2.2d:%2.2d:%2.2d</time>\n", > + fprintf(log_file->fp, "%*s<time>%2.2d:%2.2d:%2.2d</time>\n", > (xml_stack_index + 1) * XML_INDENT, > "", tm.tm_hour, tm.tm_min, tm.tm_sec); > > - fprintf(log->fp, "%*s<field_type>%s</field_type>\n", > + fprintf(log_file->fp, "%*s<field_type>%s</field_type>\n", > (xml_stack_index + 1) * XML_INDENT, > "", fwts_log_field_to_str_full(field)); > > @@ -86,26 +85,23 @@ static int fwts_log_vprintf_xml(fwts_log *log, > if (!strcmp(str, " ")) > str = "None"; > > - fprintf(log->fp, "%*s<level>%s</level>\n", > + fprintf(log_file->fp, "%*s<level>%s</level>\n", > (xml_stack_index + 1) * XML_INDENT, "", str); > > - fprintf(log->fp, "%*s<status>%s</status>\n", > + fprintf(log_file->fp, "%*s<status>%s</status>\n", > (xml_stack_index + 1) * XML_INDENT, > "", *status ? status : "None"); > > - fprintf(log->fp, "%*s<failure_label>%s</failure_label>\n", > + fprintf(log_file->fp, "%*s<failure_label>%s</failure_label>\n", > (xml_stack_index + 1) * XML_INDENT, > "", label && *label ? label : "None"); > > - vsnprintf(buffer, sizeof(buffer), fmt, args); > - fprintf(log->fp, "%*s<log_text>%s</log_text>\n", > + fprintf(log_file->fp, "%*s<log_text>%s</log_text>\n", > (xml_stack_index + 1) * XML_INDENT, > "", buffer); > > - fprintf(log->fp, "%*s</logentry>\n", xml_stack_index * XML_INDENT, ""); > - fflush(log->fp); > - > - log->line_number++; > + fprintf(log_file->fp, "%*s</logentry>\n", xml_stack_index * XML_INDENT, ""); > + fflush(log_file->fp); > > return 0; > } > @@ -114,7 +110,7 @@ static int fwts_log_vprintf_xml(fwts_log *log, > * fwts_log_underline_xml() > * write an underline across log, using character ch as the underline > */ > -static void fwts_log_underline_xml(fwts_log *log, const int ch) > +static void fwts_log_underline_xml(fwts_log_file *log_file, const int ch) > { > /* No-op for xml */ > } > @@ -123,17 +119,17 @@ static void fwts_log_underline_xml(fwts_log *log, const int ch) > * fwts_log_newline() > * write newline to log > */ > -static void fwts_log_newline_xml(fwts_log *log) > +static void fwts_log_newline_xml(fwts_log_file *log_file) > { > /* No-op for xml */ > } > > -static void fwts_log_section_begin_xml(fwts_log *log, const char *name) > +static void fwts_log_section_begin_xml(fwts_log_file *log_file, const char *name) > { > xml_stack[xml_stack_index].name = name; > > - fprintf(log->fp, "%*s<%s>\n", xml_stack_index * XML_INDENT, "", name); > - fflush(log->fp); > + fprintf(log_file->fp, "%*s<%s>\n", xml_stack_index * XML_INDENT, "", name); > + fflush(log_file->fp); > > if (xml_stack_index < MAX_XML_STACK) > xml_stack_index++; > @@ -143,13 +139,13 @@ static void fwts_log_section_begin_xml(fwts_log *log, const char *name) > } > } > > -static void fwts_log_section_end_xml(fwts_log *log) > +static void fwts_log_section_end_xml(fwts_log_file *log_file) > { > if (xml_stack_index > 0) { > xml_stack_index--; > - fprintf(log->fp, "%*s</%s>\n", xml_stack_index * XML_INDENT, > + fprintf(log_file->fp, "%*s</%s>\n", xml_stack_index * XML_INDENT, > "", xml_stack[xml_stack_index].name); > - fflush(log->fp); > + fflush(log_file->fp); > } else { > fprintf(stderr, "xml log stack underflow.\n"); > exit(EXIT_FAILURE); > @@ -157,26 +153,26 @@ static void fwts_log_section_end_xml(fwts_log *log) > > } > > -static void fwts_log_open_xml(fwts_log *log) > +static void fwts_log_open_xml(fwts_log_file *log_file) > { > char *xml_header = "<?xml version=\"1.0\" encoding=\"UTF-8\" ?>\n"; > > - fwrite(xml_header, 1, strlen(xml_header), log->fp); > - fflush(log->fp); > + fwrite(xml_header, 1, strlen(xml_header), log_file->fp); > + fflush(log_file->fp); > > - fwts_log_section_begin_xml(log, "fwts"); > + fwts_log_section_begin_xml(log_file, "fwts"); > } > > -static void fwts_log_close_xml(fwts_log *log) > +static void fwts_log_close_xml(fwts_log_file *log_file) > { > - fwts_log_section_end_xml(log); > + fwts_log_section_end_xml(log_file); > > - fwrite("\n", 1, 1, log->fp); > - fflush(log->fp); > + fwrite("\n", 1, 1, log_file->fp); > + fflush(log_file->fp); > } > > fwts_log_ops fwts_log_xml_ops = { > - .vprintf = fwts_log_vprintf_xml, > + .print = fwts_log_print_xml, > .underline = fwts_log_underline_xml, > .newline = fwts_log_newline_xml, > .section_begin = fwts_log_section_begin_xml, Gave this a spin with fwts -b --log-type html,plaintext ... results can be found here: http://ouwish.com/~vanhoof/pickup/cking/fwts_2012-06-11/ Tested-by: Chris Van Hoof <vanhoof@canonical.com>
On Mon, Jun 11, 2012 at 6:00 PM, Colin King <colin.king@canonical.com> wrote: > From: Colin Ian King <colin.king@canonical.com> > > Since we now can output many different log types we should also > allow for multiple log types to be written during a run.  This involves > some considerable re-working of the logging engine. > > 1. The fw->log_type is now a  bit map of log_types > 2. We add a list of log types to be written to fwts_log, this is a list >  of fwts_log_file types. > 3. We need to re-work the log name handling so that we can open multiple >  log files with different suffixes depending on the log type. > 4. To reduce the amount of vsnprintf() of the formatted log output we >  now handle this in the log fwts_log_printf() and pass down the formatted >  output to the different logging handlers rather than keep on re-formatting >  at the lowest logging handler layer. > > There are a lot of changing is this patch. I tried to break it down, but > since there are so many interdependant changes I had to resort to one big > patch > > Signed-off-by: Colin Ian King <colin.king@canonical.com> > --- >  src/lib/include/fwts_log.h    |  66 +++++-- >  src/lib/src/fwts_framework.c   |  94 +++++----- >  src/lib/src/fwts_log.c      |  363 +++++++++++++++++++++++++++++--------- >  src/lib/src/fwts_log_html.c    |  133 +++++++------- >  src/lib/src/fwts_log_json.c    |  52 +++--- >  src/lib/src/fwts_log_plaintext.c |  63 ++++--- >  src/lib/src/fwts_log_xml.c    |  72 ++++---- >  7 files changed, 527 insertions(+), 316 deletions(-) > > diff --git a/src/lib/include/fwts_log.h b/src/lib/include/fwts_log.h > index a659baa..513bf88 100644 > --- a/src/lib/include/fwts_log.h > +++ b/src/lib/include/fwts_log.h > @@ -23,7 +23,10 @@ >  #include <stdio.h> >  #include <stdarg.h> > > -#define LOG_MAGIC    0xfe23ab13 > +#include "fwts_list.h" > + > +#define LOG_MAGIC        (0xfe23ab13) > +#define LOG_MAX_BUF_SIZE    (4096)      /* Max output per log line */ > >  typedef enum { >     LOG_RESULT      = 0x00000001, > @@ -58,31 +61,56 @@ typedef enum { >     LOG_LEVEL_INFO    = 0x00000010, >  } fwts_log_level; > > +/* > + *  different types of log file > + */ >  typedef enum { >     LOG_TYPE_NONE    = 0x00000000, >     LOG_TYPE_PLAINTEXT  = 0x00000001, >     LOG_TYPE_JSON    = 0x00000002, > -    LOG_TYPE_XML     = 0x00000003, > -    LOG_TYPE_HTML    = 0x00000004, > +    LOG_TYPE_XML     = 0x00000004, > +    LOG_TYPE_HTML    = 0x00000008, >  } fwts_log_type; > > +/* > + *  different types of output log > + */ > +typedef enum { > +    LOG_FILENAME_TYPE_STDOUT = 0x00000001,  /* log output to stdout */ > +    LOG_FILENAME_TYPE_STDERR = 0x00000002,  /* log output to stderr */ > +    LOG_FILENAME_TYPE_FILE  = 0x00000003,  /* log output to a file */ > +} fwts_log_filename_type; > + > +/* > + *  top level log descriptor > + */ >  typedef struct log_t { > -    unsigned int magic; > -    FILE *fp; > -    char *owner; > -    int line_width; > -    int line_number; > -    struct fwts_log_ops_t *ops; > +    unsigned int magic;           /* magic ID of the log */ > +    fwts_list log_files;           /* list of fwts_log_file */ > +    int line_number;             /* keeps track of the line numbering */ > +    char *owner;               /* who is writing to this log */ >  } fwts_log; > > +/* > + *  info for a specific log type > + */ > +typedef struct { > +    FILE *fp;                /* file descriptor for log */ > +    fwts_log *log;              /* parent log struct */ > +    fwts_log_type type;           /* log type */ > +    fwts_log_filename_type filename_type;  /* log filename type */ > +    struct fwts_log_ops_t *ops;       /* log operators */ > +    int line_width;             /* width of log in chars */ > +} fwts_log_file; > + >  typedef struct fwts_log_ops_t { > -    int (*vprintf)(fwts_log *log, const fwts_log_field field, const fwts_log_level level, const char *status, const char *label, const char *prefix, const char *fmt, va_list args); > -    void (*underline)(fwts_log *log, int ch); > -    void (*newline)(fwts_log *log); > -    void (*section_begin)(fwts_log *, const char *tag); > -    void (*section_end)(fwts_log *); > -    void (*open)(fwts_log *); > -    void (*close)(fwts_log *); > +    int (*print)(fwts_log_file *log_file, const fwts_log_field field, const fwts_log_level level, const char *status, const char *label, const char *prefix, const char *buffer); > +    void (*underline)(fwts_log_file *log_file, int ch); > +    void (*newline)(fwts_log_file *log_file); > +    void (*section_begin)(fwts_log_file *log_file, const char *tag); > +    void (*section_end)(fwts_log_file *log_file); > +    void (*open)(fwts_log_file *log_file); > +    void (*close)(fwts_log_file *log_file); >  } fwts_log_ops; > >  extern fwts_log_ops fwts_log_plaintext_ops; > @@ -116,6 +144,12 @@ int      fwts_log_line_number(fwts_log *log); >  void   fwts_log_set_line_width(const int width); >  void   fwts_log_section_begin(fwts_log *log, const char *name); >  void   fwts_log_section_end(fwts_log *log); > +fwts_log_filename_type fwts_log_get_filename_type(const char *name); > + > +static inline int fwts_log_type_count(fwts_log_type type) > +{ > +    return __builtin_popcount(type); > +} > >  #define fwts_log_result(fw, fmt, args...)    \ >     fwts_log_printf(fw->results, LOG_RESULT, LOG_LEVEL_NONE, "", "", "", fmt, ## args) > diff --git a/src/lib/src/fwts_framework.c b/src/lib/src/fwts_framework.c > index f808d8e..a6c2ba2 100644 > --- a/src/lib/src/fwts_framework.c > +++ b/src/lib/src/fwts_framework.c > @@ -28,7 +28,8 @@ > >  #include "fwts.h" > > -#define RESULTS_LOG   "results.log" > +/* Suffix ".log", ".xml", etc gets automatically appended */ > +#define RESULTS_LOG   "results" > >  #define FWTS_RUN_ALL_FLAGS       \ >     (FWTS_BATCH |          \ > @@ -96,34 +97,6 @@ static const char *fwts_copyright[] = { >  }; > >  /* > - *  fwts_framework_log_suffix() > - *   set the log name suffix > - */ > -static void fwts_framework_log_suffix(fwts_framework *fw, const char *suffix) > -{ > -    char *ptr; > -    char *new; > -    size_t len; > - > -    /* Locate old suffix and kill it */ > -    ptr = rindex(fw->results_logname, '.'); > -    if (ptr != NULL) > -        *ptr = '\0'; > - > -    /* Space for old log name sans old suffix + new suffix + '.' + '\0' */ > -    len = strlen(fw->results_logname) + strlen(suffix) + 2; > - > -    if ((new = calloc(len, 1)) == NULL) { > -        fprintf(stderr, "Cannot allocate log name.\n"); > -        exit(EXIT_FAILURE); > -    } > - > -    snprintf(new, len, "%s.%s", fw->results_logname, suffix); > -    free(fw->results_logname); > -    fw->results_logname = new; > -} > - > -/* >  *  fwts_framework_compare_priority() >  *   used to register tests sorted on run priority >  */ > @@ -677,7 +650,7 @@ static fwts_framework_test *fwts_framework_test_find(fwts_framework *fw, const c >  *  fwts_framework_log() >  *   log a test result >  */ > -void fwts_framework_log(fwts_framework *fw, > +void fwts_framework_log(fwts_framework *fw, >     fwts_log_field field, >     const char *label, >     fwts_log_level level, > @@ -860,6 +833,39 @@ static int fwts_framework_skip_test_parse(fwts_framework *fw, const char *arg, f >     return FWTS_OK; >  } > > +/* > + *  fwts_framework_log_type_parse() > + *   parse optarg of comma separated log types > + */ > +static int fwts_framework_log_type_parse(fwts_framework *fw, const char *arg) > +{ > +    char *str; > +    char *token; > +    char *saveptr = NULL; > + > +    fw->log_type = 0; > + > +    for (str = (char*)arg; (token = strtok_r(str, ",", &saveptr)) != NULL; str = NULL) { > +        if (!strcmp(token, "plaintext")) > +            fw->log_type |= LOG_TYPE_PLAINTEXT; > +        else if (!strcmp(token, "json")) > +            fw->log_type |= LOG_TYPE_JSON; > +        else if (!strcmp(token, "xml")) > +            fw->log_type |= LOG_TYPE_XML; > +        else if (!strcmp(token, "html")) > +            fw->log_type |= LOG_TYPE_HTML; > +        else { > +            fprintf(stderr, "--log-type can be plaintext, xml, html or json.\n"); > +            return FWTS_ERROR; > +        } > +    } > + > +    if (!fw->log_type) > +        fw->log_type = LOG_TYPE_PLAINTEXT; > + > +    return FWTS_OK; > +} > + >  int fwts_framework_options_handler(fwts_framework *fw, int argc, char * const argv[], int option_char, int long_index) >  { >     switch (option_char) { > @@ -975,22 +981,8 @@ int fwts_framework_options_handler(fwts_framework *fw, int argc, char * const ar >             fwts_iasl_disassemble_all_to_file(fw); >             return FWTS_COMPLETE; >         case 32: /* --log-type */ > -            if (!strcmp(optarg, "plaintext")) { > -                fw->log_type = LOG_TYPE_PLAINTEXT; > -                fwts_framework_log_suffix(fw, "log"); > -            } else if (!strcmp(optarg, "json")) { > -                fw->log_type = LOG_TYPE_JSON; > -                fwts_framework_log_suffix(fw, "json"); > -            } else if (!strcmp(optarg, "xml")) { > -                fw->log_type = LOG_TYPE_XML; > -                fwts_framework_log_suffix(fw, "xml"); > -            } else if (!strcmp(optarg, "html")) { > -                fw->log_type = LOG_TYPE_HTML; > -                fwts_framework_log_suffix(fw, "html"); > -            } else { > -                fprintf(stderr, "--log-type can be either plaintext, xml, html or json.\n"); > -                return FWTS_ERROR; > -            } > +            fwts_framework_log_type_parse(fw, optarg); > +            /* FIX ME - check return */ >             break; >         } >         break; > @@ -1136,6 +1128,16 @@ int fwts_framework_args(const int argc, char **argv) >         goto tidy_close; >     } > > +    /* Ensure we have just one log type specified for non-filename logging */ > +    if (fwts_log_type_count(fw->log_type) > 1 && > +      fwts_log_get_filename_type(fw->results_logname) != LOG_FILENAME_TYPE_FILE) { > +        fprintf(stderr, > +            "Cannot specify more than one log type when " > +            "logging to stderr or stdout\n"); > +        ret = FWTS_ERROR; > +        goto tidy_close; > +    } > + >     /* Results log */ >     if ((fw->results = fwts_log_open("fwts", >             fw->results_logname, > diff --git a/src/lib/src/fwts_log.c b/src/lib/src/fwts_log.c > index fea7c41..3ab7930 100644 > --- a/src/lib/src/fwts_log.c > +++ b/src/lib/src/fwts_log.c > @@ -315,48 +315,125 @@ void fwts_log_set_format(const char *str) >  } > >  /* > - *  fwts_log_vprintf() > - *   printf to a log > + *  fwts_log_type_filename_suffix() > + *   return a filename suffix on a given log type >  */ > -int fwts_log_printf(fwts_log *log, > -    const fwts_log_field field, > -    const fwts_log_level level, > -    const char *status, > -    const char *label, > -    const char *prefix, > -    const char *fmt, ...) > +static char *fwts_log_type_filename_suffix(fwts_log_type type) >  { > -    va_list args; > -    int ret; > +    switch (type) { > +    case LOG_TYPE_JSON: > +        return ".json"; > +    case LOG_TYPE_XML: > +        return ".xml"; > +    case LOG_TYPE_HTML: > +        return ".html"; > +    case LOG_TYPE_NONE: > +    case LOG_TYPE_PLAINTEXT: > +    default: > +        return ".log"; > +    } > +} > + > +/* > + *  fwts_log_filename_new_suffix() > + *   return the log name with suffix based on log type > + */ > +static char *fwts_log_filename(const char *filename, fwts_log_type type) > +{ > +    char *ptr; > +    char *new_name; > +    char *suffix; > +    size_t suffix_len; > +    size_t trunc_len; > +    size_t filename_len; > + > +    suffix = fwts_log_type_filename_suffix(type); > +    suffix_len = strlen(suffix); > + > +    /* > +     * Locate an existing suffix, if it is one we recognise > +     * then remove it and append the appropriate one > +     */ > +    ptr = rindex(filename, '.'); > +    if (ptr && > +        (!strcmp(ptr, ".log") || > +         !strcmp(ptr, ".json") || > +         !strcmp(ptr, ".xml") || > +         !strcmp(ptr, ".html"))) { > + > +        trunc_len = ptr - filename; > +        if ((new_name = calloc(trunc_len + suffix_len + 1, 1)) == NULL) { > +            fprintf(stderr, "Cannot allocate log name.\n"); > +            return NULL; > +        } > +        strncpy(new_name, filename, trunc_len); > +        strcat(new_name, suffix); /* strcat OK because calloc zero'd all of new_name */ > +        return new_name; > +    } > > -    va_start(args, fmt); > -    ret = fwts_log_vprintf(log, field, level, status, label, prefix, fmt, args); > -    va_end(args); > +    /* > +     * We didn't find a suffix or a known suffix, so append > +     * the appropriate one to the given log filename > +     */ > +    filename_len = strlen(filename); > +    if ((new_name = calloc(filename_len + suffix_len + 1, 1)) == NULL) { > +        fprintf(stderr, "Cannot allocate log name.\n"); > +        return NULL; > +    } > > -    return ret; > +    strcpy(new_name, filename); > +    strcat(new_name, suffix); > + > +    return new_name; >  } > >  /* >  *  fwts_log_vprintf() > - *   vprintf to a log > + *   printf to a log >  */ > -int fwts_log_vprintf(fwts_log *log, > +int fwts_log_printf(fwts_log *log, >     const fwts_log_field field, >     const fwts_log_level level, >     const char *status, >     const char *label, >     const char *prefix, > -    const char *fmt, > -    va_list args) > +    const char *fmt, ...) >  { > +    va_list args; > +    int ret = 0; > + > +    char buffer[LOG_MAX_BUF_SIZE]; > + >     if (!((field & LOG_FIELD_MASK) & fwts_log_filter)) > -        return 0; > +        return ret; > + > +    if (log && log->magic == LOG_MAGIC) { > +        fwts_list_link *item; > + > +        /* > +         * With the possibility of having multiple logs being written > +         * to per call of fwts_log_printf() it is more efficient to > +         * vsnprintf() here and then pass the formatted output down to > +         * each log handler rather than re-formatting each time in each > +         * handler > +         */ > +        va_start(args, fmt); > +        ret = vsnprintf(buffer, sizeof(buffer), fmt, args); > +        if (ret < 0) > +            return ret; > + > +        fwts_list_foreach(item, &log->log_files) { > +            fwts_log_file *log_file = fwts_list_data(fwts_log_file *, item); > + > +            if (log_file->ops && log_file->ops->print) > +                log_file->ops->print(log_file, field, level, > +                    status, label, prefix, buffer); > +        } > +        log->line_number++; > > -    if (log && log->magic == LOG_MAGIC && > -      log->ops && log->ops->underline) > -        return log->ops->vprintf(log, field, level, status, label, prefix, fmt, args); > -    else > -        return 0; > +        va_end(args); > +    } > +    return ret; >  } > >  /* > @@ -365,9 +442,16 @@ int fwts_log_vprintf(fwts_log *log, >  */ >  void fwts_log_underline(fwts_log *log, const int ch) >  { > -    if (log && log->magic == LOG_MAGIC && > -      log->ops && log->ops->underline) > -        log->ops->underline(log, ch); > +    if (log && log->magic == LOG_MAGIC) { > +        fwts_list_link *item; > + > +        fwts_list_foreach(item, &log->log_files) { > +            fwts_log_file *log_file = fwts_list_data(fwts_log_file *, item); > + > +            if (log_file->ops && log_file->ops->underline) > +                log_file->ops->underline(log_file, ch); > +        } > +    } >  } > >  /* > @@ -376,9 +460,17 @@ void fwts_log_underline(fwts_log *log, const int ch) >  */ >  void fwts_log_newline(fwts_log *log) >  { > -    if (log && log->magic == LOG_MAGIC && > -      log->ops && log->ops->underline) > -        log->ops->newline(log); > +    if (log && log->magic == LOG_MAGIC) { > +        fwts_list_link *item; > + > +        fwts_list_foreach(item, &log->log_files) { > +            fwts_log_file *log_file = fwts_list_data(fwts_log_file *, item); > + > +            if (log_file->ops && log_file->ops->newline) > +                log_file->ops->newline(log_file); > +        } > +        log->line_number++; > +    } >  } > >  int fwts_log_set_owner(fwts_log *log, const char *owner) > @@ -395,96 +487,199 @@ int fwts_log_set_owner(fwts_log *log, const char *owner) >     return FWTS_ERROR; >  } > > + > +/* > + *  fwts_log_section_begin() > + *   mark a start of a named section.  For structured logging > + *   such as XML and JSON this pushes a new named tagged section > + */ >  void fwts_log_section_begin(fwts_log *log, const char *name) >  { > -    if (log && log->magic == LOG_MAGIC && > -      log->ops && log->ops->section_begin) > -        log->ops->section_begin(log, name); > +    if (log && log->magic == LOG_MAGIC) { > +        fwts_list_link *item; > + > +        fwts_list_foreach(item, &log->log_files) { > +            fwts_log_file *log_file = fwts_list_data(fwts_log_file *, item); > + > +            if (log_file->ops && log_file->ops->section_begin) > +                log_file->ops->section_begin(log_file, name); > +        } > +    } >  } > > +/* > + *  fwts_log_section_end() > + *   mark end of a named section.  For structured logging > + *   such as XML and JSON this pops the end of a tagged section > + */ >  void fwts_log_section_end(fwts_log *log) >  { > -    if (log && log->magic == LOG_MAGIC && > -      log->ops && log->ops->section_end) > -        log->ops->section_end(log); > +    if (log && log->magic == LOG_MAGIC) { > +        fwts_list_link *item; > + > +        fwts_list_foreach(item, &log->log_files) { > +            fwts_log_file *log_file = fwts_list_data(fwts_log_file *, item); > + > +            if (log_file->ops && log_file->ops->section_end) > +                log_file->ops->section_end(log_file); > +        } > +    } >  } > >  /* > - *  fwts_log_open() > - *   open a log file. if name is stderr or stdout, then attach log to these > - *   streams. > + *  fwts_log_get_ops() > + *   return log ops basedon log type >  */ > -fwts_log *fwts_log_open(const char *owner, const char *name, const char *mode, fwts_log_type type) > +static fwts_log_ops *fwts_log_get_ops(fwts_log_type type) >  { > -    fwts_log *newlog; > - > -    if ((newlog = calloc(1, sizeof(fwts_log))) == NULL) > -        return NULL; > - > -    newlog->magic = LOG_MAGIC; >     switch (type) { >     case LOG_TYPE_JSON: > -        newlog->ops = &fwts_log_json_ops; > -        break; > +        return &fwts_log_json_ops; >     case LOG_TYPE_PLAINTEXT: > -        newlog->ops = &fwts_log_plaintext_ops; > -        break; > +        return &fwts_log_plaintext_ops; >     case LOG_TYPE_XML: > -        newlog->ops = &fwts_log_xml_ops; > -        break; > +        return &fwts_log_xml_ops; >     case LOG_TYPE_HTML: > -        newlog->ops = &fwts_log_html_ops; > -        break; > +        return &fwts_log_html_ops; >     case LOG_TYPE_NONE: >     default: > -        newlog->ops = &fwts_log_plaintext_ops; > -        break; > +        return &fwts_log_plaintext_ops; >     } > +} > > -    if (owner) { > -        if ((newlog->owner = calloc(1, strlen(owner)+1)) == NULL) { > -            free(newlog); > -            return NULL; > -        } > -        strcpy(newlog->owner, owner); > -    } > +/* > + *  fwts_log_get_filename_type() > + *   determine the filename type > + */ > +fwts_log_filename_type fwts_log_get_filename_type(const char *filename) > +{ > +    if (!strcmp(filename, "stderr")) > +        return LOG_FILENAME_TYPE_STDERR; > +    else if (!strcmp(filename, "stdout")) > +        return LOG_FILENAME_TYPE_STDOUT; > +    else > +        return LOG_FILENAME_TYPE_FILE; > +} > + > +/* > + *  fwts_log_open() > + *   open a log file. if name is stderr or stdout, then attach log to these > + *   streams. > + */ > +fwts_log *fwts_log_open( > +    const char *owner,    /* Creator of the log */ > +    const char *filename,  /* Log file name */ > +    const char *mode,    /* open mode, see fopen() modes */ > +    fwts_log_type type)   /* Log type */ > +{ > +    fwts_log *newlog; > +    unsigned int i; > +    char *newname; > > -    if (strcmp("stderr", name) == 0) > -        newlog->fp = stderr; > -    else if (strcmp("stdout", name) == 0) > -        newlog->fp = stdout; > -    else if ((newlog->fp = fopen(name, mode)) == NULL) { > -        free(newlog); > +    if ((newlog = calloc(1, sizeof(fwts_log))) == NULL) >         return NULL; > -    } > > -    if (log_line_width) { > -        /* User has specified width, so use it */ > -        newlog->line_width = log_line_width; > -    } else { > -        newlog->line_width = fwts_tty_width(fileno(newlog->fp), LOG_LINE_WIDTH); > -    } > +    newlog->magic = LOG_MAGIC; > > -    if (newlog->ops && newlog->ops->open) > -        newlog->ops->open(newlog); > +    fwts_log_set_owner(newlog, owner); > +    fwts_list_init(&newlog->log_files); > + > +    /* > +     *  Scan through and see which log types have been specified > +     *  and open the log file with the appropriate ops to perform > +     *  the logging > +     */ > +    for (i=0; i<32; i++) { > +        fwts_log_type mask = 1 << i;   /* The log type for this iteration */ > + > +        /* If set then go and open up a log for this log type */ > +        if (type & mask) { > +            fwts_log_file *log_file; > + > +            if ((log_file = calloc(1, sizeof(fwts_log_file))) == NULL) { > +                fwts_log_close(newlog); > +                return NULL; > +            } > + > +            log_file->type = mask; > +            log_file->ops  = fwts_log_get_ops(mask); > +            log_file->log  = newlog; > +            log_file->filename_type = fwts_log_get_filename_type(filename); > + > +            /* > +             *  To complicate matters we can have logs being > +             *  written to stderr, stdout or two a named file > +             */ > +            switch(log_file->filename_type) { > +            case LOG_FILENAME_TYPE_STDERR: > +                log_file->fp = stderr; > +                break; > +            case LOG_FILENAME_TYPE_STDOUT: > +                log_file->fp = stdout; > +                break; > +            case LOG_FILENAME_TYPE_FILE: > +                if ((newname = fwts_log_filename(filename, mask)) == NULL) { > +                    fwts_log_close(newlog); > +                    return NULL; > +                } > +                log_file->fp = fopen(newname, mode); > +                free(newname); > + > +                if (log_file->fp == NULL) { > +                    fwts_log_close(newlog); > +                    return NULL; > +                } > +            } > + > +            /* Fix up the log specific line width */ > +            if (log_line_width) { > +                /* User has specified width, so use it */ > +                log_file->line_width = log_line_width; > +            } else { > +                log_file->line_width = > +                    fwts_tty_width(fileno(log_file->fp), LOG_LINE_WIDTH); > +            } > + > +            /* ..and add the log file to the list of logs */ > +            fwts_list_append(&newlog->log_files, log_file); > + > +            /* ..and do the log specific opening set up */ > +            if (log_file->ops && log_file->ops->open) > +                log_file->ops->open(log_file); > +        } > +    } > >     return newlog; >  } > >  /* >  *  fwts_log_close() > - *   close a log file > + *   close any opened log files, free up memory >  */ >  int fwts_log_close(fwts_log *log) >  { >     if (log && (log->magic == LOG_MAGIC)) { > -        if (log->ops && log->ops->close) > -            log->ops->close(log); > +        fwts_list_link *item; > + > +        fwts_list_foreach(item, &log->log_files) { > +            fwts_log_file *log_file = fwts_list_data(fwts_log_file *, item); > + > +            /* Do the log type specific close */ > +            if (log_file->ops && log_file->ops->close) > +                log_file->ops->close(log_file); > + > +            /* Close opened log file */ > +            if (log_file->fp && > +              log_file->filename_type == LOG_FILENAME_TYPE_FILE) > +                fclose(log_file->fp); > +        } > + > +        /* ..and free log files */ > +        fwts_list_free_items(&log->log_files, free); > > -        if (log->fp && (log->fp != stdout && log->fp != stderr)) > -            fclose(log->fp); >         if (log->owner) >             free(log->owner); > + >         free(log); >     } >     return FWTS_OK; > diff --git a/src/lib/src/fwts_log_html.c b/src/lib/src/fwts_log_html.c > index a70ac21..1ca79b8 100644 > --- a/src/lib/src/fwts_log_html.c > +++ b/src/lib/src/fwts_log_html.c > @@ -37,31 +37,31 @@ typedef struct { >  static fwts_log_html_stack_t html_stack[MAX_HTML_STACK]; >  static int html_stack_index = 0; > > -static void fwts_log_html(fwts_log *log, const char *fmt, ...) > +static void fwts_log_html(fwts_log_file *log_file, const char *fmt, ...) >  { >     va_list args; > >     va_start(args, fmt); > > -    fprintf(log->fp, "%*s", html_stack_index * HTML_INDENT, ""); > -    vfprintf(log->fp, fmt, args); > +    fprintf(log_file->fp, "%*s", html_stack_index * HTML_INDENT, ""); > +    vfprintf(log_file->fp, fmt, args); > >     va_end(args); >  } > > >  /* > - *  fwts_log_vprintf_html() > - *   vprintf to a log > + *  fwts_log_print_html() > + *   print to a log >  */ > -static int fwts_log_vprintf_html(fwts_log *log, > +static int fwts_log_print_html( > +    fwts_log_file *log_file, >     const fwts_log_field field, >     const fwts_log_level level, >     const char *status, >     const char *label, >     const char *prefix, > -    const char *fmt, > -    va_list args) > +    const char *buffer) >  { >     char *str; >     char *style; > @@ -74,7 +74,7 @@ static int fwts_log_vprintf_html(fwts_log *log, >     if (field & (LOG_NEWLINE | LOG_SEPARATOR | LOG_DEBUG)) >         return 0; > > -    fwts_log_html(log, "<TR>\n"); > +    fwts_log_html(log_file, "<TR>\n"); > >     if (field & LOG_VERBATUM) { >         code_start = "<PRE class=style_code>"; > @@ -86,29 +86,24 @@ static int fwts_log_vprintf_html(fwts_log *log, > >     switch (field & LOG_FIELD_MASK) { >     case LOG_ERROR: > -        fwts_log_html(log, "  <TD class=style_error>Error</TD><TD COLSPAN=2>"); > -        vfprintf(log->fp, fmt, args); > -        fprintf(log->fp, "</TD>\n"); > +        fwts_log_html(log_file, "  <TD class=style_error>Error</TD>" > +            "<TD COLSPAN=2>%s</TD>\n", buffer); >         break; >     case LOG_WARNING: > -        fwts_log_html(log, "  <TD class=style_error>Warning</TD><TD COLSPAN=2 class=style_advice_info>%s", code_start); > -        vfprintf(log->fp, fmt, args); > -        fprintf(log->fp, "%s</TD>\n", code_end); > +        fwts_log_html(log_file, "  <TD class=style_error>Warning</TD>" > +            "<TD COLSPAN=2 class=style_advice_info>%s%s%s</TD>\n", > +            code_start, buffer, code_end); >         break; >     case LOG_HEADING: > -        fwts_log_html(log, "<TD COLSPAN=2 class=style_heading>%s", code_start); > -        vfprintf(log->fp, fmt, args); > -        fprintf(log->fp, "%s</TD>\n", code_end); > +        fwts_log_html(log_file, "<TD COLSPAN=2 class=style_heading>%s%s%s</TD>\n", > +            code_start, buffer, code_end); >         break; >     case LOG_INFO: > -        fwts_log_html(log, "  <TD></TD><TD COLSPAN=2 class=style_infos>%s", code_start); > -        vfprintf(log->fp, fmt, args); > -        fprintf(log->fp, "%s</TD>\n", code_end); > +        fwts_log_html(log_file, "  <TD></TD><TD COLSPAN=2 class=style_infos>%s%s%s</TD>\n", > +            code_start, buffer, code_end); >         break; >     case LOG_PASSED: > -        fwts_log_html(log, "<TD class=style_passed>PASSED</TD><TD>"); > -        vfprintf(log->fp, fmt, args); > -        fprintf(log->fp, "</TD>\n"); > +        fwts_log_html(log_file, "<TD class=style_passed>PASSED</TD><TD>%s</TD>\n", buffer); >         break; >     case LOG_FAILED: >         switch (level) { > @@ -132,39 +127,33 @@ static int fwts_log_vprintf_html(fwts_log *log, >         } >         str = fwts_log_level_to_str(level); > > -        fwts_log_html(log, "  <TD%s>%s [%s]</TD>\n", style, *status ? status : "", str); > - > -        fwts_log_html(log, "  <TD>"); > -        vfprintf(log->fp, fmt, args); > -        fprintf(log->fp, "</TD>\n"); > +        fwts_log_html(log_file, "  <TD%s>%s [%s]</TD>\n", style, *status ? status : "", str); > +        fwts_log_html(log_file, "  <TD>%s</TD>\n", buffer); >         break; > >     case LOG_SKIPPED: > -        fwts_log_html(log, "<TD class=style_skipped>Skipped</TD><TD>%s", code_start); > -        vfprintf(log->fp, fmt, args); > -        fprintf(log->fp, "%s</TD>\n", code_end); > +        fwts_log_html(log_file, "<TD class=style_skipped>Skipped</TD>" > +            "<TD>%s%s%s</TD>\n", code_start, buffer, code_end); >         break; > >     case LOG_SUMMARY: > -        fwts_log_html(log, "  <TD></TD><TD COLSPAN=2 class=style_summary>%s", code_start); > -        vfprintf(log->fp, fmt, args); > -        fprintf(log->fp, "%s</TD>\n", code_end); > +        fwts_log_html(log_file, "  <TD></TD>" > +            "<TD COLSPAN=2 class=style_summary>%s%s%s</TD>\n", > +            code_start, buffer, code_end); >         break; > >     case LOG_ADVICE: > -        fwts_log_html(log, "  <TD class=style_advice>Advice</TD><TD COLSPAN=2 class=style_advice_info>%s", code_start); > -        vfprintf(log->fp, fmt, args); > -        fprintf(log->fp, "%s</TD>\n", code_end); > +        fwts_log_html(log_file, "  <TD class=style_advice>Advice</TD>" > +            "<TD COLSPAN=2 class=style_advice_info>%s%s%s</TD>\n", > +            code_start, buffer, code_end); >         break; > >     default: >         break; >     } > > -    fwts_log_html(log, "</TR>\n"); > -    fflush(log->fp); > - > -    log->line_number++; > +    fwts_log_html(log_file, "</TR>\n"); > +    fflush(log_file->fp); > >     return 0; >  } > @@ -173,7 +162,7 @@ static int fwts_log_vprintf_html(fwts_log *log, >  *  fwts_log_underline_html() >  *   write an underline across log, using character ch as the underline >  */ > -static void fwts_log_underline_html(fwts_log *log, const int ch) > +static void fwts_log_underline_html(fwts_log_file *log_file, const int ch) >  { >     /* No-op for html */ >  } > @@ -182,26 +171,26 @@ static void fwts_log_underline_html(fwts_log *log, const int ch) >  *  fwts_log_newline() >  *   write newline to log >  */ > -static void fwts_log_newline_html(fwts_log *log) > +static void fwts_log_newline_html(fwts_log_file *log_file) >  { >     /* No-op for html */ >  } > > -static void fwts_log_section_begin_html(fwts_log *log, const char *name) > +static void fwts_log_section_begin_html(fwts_log_file *log_file, const char *name) >  { >     html_stack[html_stack_index].name = name; > >     if (!strcmp(name, "summary")) { > -        fwts_log_html(log, "<TR><TD class=style_heading COLSPAN=2>Summary</TD></TR>\n"); > +        fwts_log_html(log_file, "<TR><TD class=style_heading COLSPAN=2>Summary</TD></TR>\n"); >     } else if (!strcmp(name, "heading")) { > -        fwts_log_html(log, "<TR><TD class=style_heading COLSPAN=2>Firmware Test Suite</TD></TR>\n"); > +        fwts_log_html(log_file, "<TR><TD class=style_heading COLSPAN=2>Firmware Test Suite</TD></TR>\n"); >     } else if (!strcmp(name, "subtest_info")) { > -        fwts_log_html(log, "<TR><TD class=style_subtest COLSPAN=2></TD></TR>\n"); > +        fwts_log_html(log_file, "<TR><TD class=style_subtest COLSPAN=2></TD></TR>\n"); >     } else if (!strcmp(name, "failure")) { > -        fwts_log_html(log, "<TR><TD class=style_heading COLSPAN=2></TD></TR>\n"); > +        fwts_log_html(log_file, "<TR><TD class=style_heading COLSPAN=2></TD></TR>\n"); >     } > > -    fflush(log->fp); > +    fflush(log_file->fp); > >     if (html_stack_index < MAX_HTML_STACK) >         html_stack_index++; > @@ -211,11 +200,11 @@ static void fwts_log_section_begin_html(fwts_log *log, const char *name) >     } >  } > > -static void fwts_log_section_end_html(fwts_log *log) > +static void fwts_log_section_end_html(fwts_log_file *log_file) >  { >     if (html_stack_index > 0) { >         html_stack_index--; > -        fflush(log->fp); > +        fflush(log_file->fp); >     } else { >         fprintf(stderr, "html log stack underflow.\n"); >         exit(EXIT_FAILURE); > @@ -223,15 +212,15 @@ static void fwts_log_section_end_html(fwts_log *log) > >  } > > -static void fwts_log_open_html(fwts_log *log) > +static void fwts_log_open_html(fwts_log_file *log_file) >  { > -    fwts_log_html(log, "<HTML>\n"); > -    fwts_log_html(log, "<HEAD>\n"); > -    fwts_log_html(log, "  <TITLE>fwts log</TITLE>\n"); > -    fwts_log_html(log, "</HEAD>\n"); > -    fwts_log_html(log, "<BODY>\n"); > -    fwts_log_html(log, "<STYLE>\n"); > -    fwts_log_html(log, > +    fwts_log_html(log_file, "<HTML>\n"); > +    fwts_log_html(log_file, "<HEAD>\n"); > +    fwts_log_html(log_file, "  <TITLE>fwts log</TITLE>\n"); > +    fwts_log_html(log_file, "</HEAD>\n"); > +    fwts_log_html(log_file, "<BODY>\n"); > +    fwts_log_html(log_file, "<STYLE>\n"); > +    fwts_log_html(log_file, >         ".style_critical { background-color: red; font-weight: bold; " >         "text-align: center; vertical-align: center  }\n" >         ".style_high { background-color: orange; font-weight: bold; " > @@ -256,27 +245,27 @@ static void fwts_log_open_html(fwts_log *log) >         ".style_info { }\n" >         ".style_code { font-family: \"courier\",\"mono\"; font-size:0.75em; overflow:auto; " >         "width:90%; line-height:0.82em; font-stretch:extra-condensed; word-wrap:normal }\n"); > -    fwts_log_html(log, "</STYLE>\n"); > -    fflush(log->fp); > +    fwts_log_html(log_file, "</STYLE>\n"); > +    fflush(log_file->fp); > > -    fwts_log_html(log, "<TABLE WIDTH=1024>\n"); > -    fwts_log_html(log, "</TR>\n"); > +    fwts_log_html(log_file, "<TABLE WIDTH=1024>\n"); > +    fwts_log_html(log_file, "</TR>\n"); > > -    fwts_log_section_begin_html(log, "fwts"); > +    fwts_log_section_begin_html(log_file, "fwts"); >  } > > -static void fwts_log_close_html(fwts_log *log) > +static void fwts_log_close_html(fwts_log_file *log_file) >  { > -    fwts_log_section_end_html(log); > +    fwts_log_section_end_html(log_file); > > -    fwts_log_html(log, "</TABLE>\n"); > -    fwts_log_html(log, "</BODY>\n"); > -    fwts_log_html(log, "</HTML>\n"); > -    fflush(log->fp); > +    fwts_log_html(log_file, "</TABLE>\n"); > +    fwts_log_html(log_file, "</BODY>\n"); > +    fwts_log_html(log_file, "</HTML>\n"); > +    fflush(log_file->fp); >  } > >  fwts_log_ops fwts_log_html_ops = { > -    .vprintf =    fwts_log_vprintf_html, > +    .print =     fwts_log_print_html, >     .underline =   fwts_log_underline_html, >     .newline =    fwts_log_newline_html, >     .section_begin = fwts_log_section_begin_html, > diff --git a/src/lib/src/fwts_log_json.c b/src/lib/src/fwts_log_json.c > index 208847c..8dd65e4 100644 > --- a/src/lib/src/fwts_log_json.c > +++ b/src/lib/src/fwts_log_json.c > @@ -40,19 +40,19 @@ static fwts_log_json_stack_t json_stack[MAX_JSON_STACK]; >  static int json_stack_index = 0; > >  /* > - *  fwts_log_vprintf_json() > - *   vprintf to a log > + *  fwts_log_printf_son() > + *   print to a log >  */ > -static int fwts_log_vprintf_json(fwts_log *log, > +static int fwts_log_print_json( > +    fwts_log_file *log_file, >     const fwts_log_field field, >     const fwts_log_level level, >     const char *status, >     const char *label, >     const char *prefix, > -    const char *fmt, > -    va_list args) > +    const char *buffer) >  { > -    char buffer[4096]; > +    char tmpbuf[4096]; >     struct tm tm; >     time_t now; >     json_object *header; > @@ -69,13 +69,13 @@ static int fwts_log_vprintf_json(fwts_log *log, >     localtime_r(&now, &tm); > >     header = json_object_new_object(); > -    json_object_object_add(header, "line_num", json_object_new_int(log->line_number)); > -    snprintf(buffer, sizeof(buffer), "%2.2d/%2.2d/%-2.2d", > +    json_object_object_add(header, "line_num", json_object_new_int(log_file->log->line_number)); > +    snprintf(tmpbuf, sizeof(tmpbuf), "%2.2d/%2.2d/%-2.2d", >         tm.tm_mday, tm.tm_mon + 1, (tm.tm_year+1900) % 100); > -    json_object_object_add(header, "date", json_object_new_string(buffer)); > -    snprintf(buffer, sizeof(buffer), "%2.2d:%2.2d:%2.2d", > +    json_object_object_add(header, "date", json_object_new_string(tmpbuf)); > +    snprintf(tmpbuf, sizeof(tmpbuf), "%2.2d:%2.2d:%2.2d", >         tm.tm_hour, tm.tm_min, tm.tm_sec); > -    json_object_object_add(header, "time", json_object_new_string(buffer)); > +    json_object_object_add(header, "time", json_object_new_string(tmpbuf)); >     json_object_object_add(header, "field_type", >         json_object_new_string(fwts_log_field_to_str_full(field))); > > @@ -92,13 +92,10 @@ static int fwts_log_vprintf_json(fwts_log *log, >     json_object_object_add(header, "owner", >         json_object_new_string(log->owner)); >     */ > -    vsnprintf(buffer, sizeof(buffer), fmt, args); >     json_object_object_add(header, "log_text", json_object_new_string(buffer)); > >     json_object_array_add(json_log, header); > > -    log->line_number++; > - >     return 0; >  } > > @@ -106,7 +103,7 @@ static int fwts_log_vprintf_json(fwts_log *log, >  *  fwts_log_underline_json() >  *   write an underline across log, using character ch as the underline >  */ > -static void fwts_log_underline_json(fwts_log *log, const int ch) > +static void fwts_log_underline_json(fwts_log_file *log_file, const int ch) >  { >     /* No-op for json */ >  } > @@ -115,12 +112,12 @@ static void fwts_log_underline_json(fwts_log *log, const int ch) >  *  fwts_log_newline() >  *   write newline to log >  */ > -static void fwts_log_newline_json(fwts_log *log) > +static void fwts_log_newline_json(fwts_log_file *log_file) >  { >     /* No-op for json */ >  } > > -static void fwts_log_section_begin_json(fwts_log *log, const char *name) > +static void fwts_log_section_begin_json(fwts_log_file *log_file, const char *name) >  { >     json_object *json_obj; >     json_object *json_log; > @@ -132,7 +129,7 @@ static void fwts_log_section_begin_json(fwts_log *log, const char *name) >     json_stack[json_stack_index].obj = json_obj; >     json_stack[json_stack_index].log = json_log; > > -    if (json_stack_index > 0) > +    if (json_stack_index > 0) >         json_object_array_add(json_stack[json_stack_index-1].log, json_obj); > >     if (json_stack_index < MAX_JSON_STACK) > @@ -143,7 +140,7 @@ static void fwts_log_section_begin_json(fwts_log *log, const char *name) >     } >  } > > -static void fwts_log_section_end_json(fwts_log *log) > +static void fwts_log_section_end_json(fwts_log_file *log_file) >  { >     if (json_stack_index > 0) >         json_stack_index--; > @@ -153,29 +150,30 @@ static void fwts_log_section_end_json(fwts_log *log) >     } >  } > > -static void fwts_log_open_json(fwts_log *log) > +static void fwts_log_open_json(fwts_log_file *log_file) >  { > -    fwts_log_section_begin_json(log, "fwts"); > +    fwts_log_section_begin_json(log_file, "fwts"); >  } > > -static void fwts_log_close_json(fwts_log *log) > +static void fwts_log_close_json(fwts_log_file *log_file) >  { >     const char *str; >     size_t len; > > -    fwts_log_section_end_json(log); > +    fwts_log_section_end_json(log_file); > >     str = json_object_to_json_string(json_stack[0].obj); >     len = strlen(str); > > -    fwrite(str, 1, len, log->fp); > -    fwrite("\n", 1, 1, log->fp); > -    fflush(log->fp); > +    fwrite(str, 1, len, log_file->fp); > +    fwrite("\n", 1, 1, log_file->fp); > +    fflush(log_file->fp); > + >     json_object_put(json_stack[0].obj); >  } > >  fwts_log_ops fwts_log_json_ops = { > -    .vprintf =    fwts_log_vprintf_json, > +    .print =     fwts_log_print_json, >     .underline =   fwts_log_underline_json, >     .newline =    fwts_log_newline_json, >     .section_begin = fwts_log_section_begin_json, > diff --git a/src/lib/src/fwts_log_plaintext.c b/src/lib/src/fwts_log_plaintext.c > index 44c443f..7381ae3 100644 > --- a/src/lib/src/fwts_log_plaintext.c > +++ b/src/lib/src/fwts_log_plaintext.c > @@ -32,7 +32,8 @@ >  *  fwts_log_header_plaintext() >  *   format up a tabulated log heading >  */ > -static int fwts_log_header_plaintext(fwts_log *log, > +static int fwts_log_header_plaintext( > +    fwts_log_file *log_file, >     char *buffer, >     const int len, >     const fwts_log_field field, > @@ -51,7 +52,7 @@ static int fwts_log_header_plaintext(fwts_log *log, >             ptr++; >             if (!strncmp(ptr, "line", 4)) { >                 n += snprintf(buffer + n, len - n, > -                    "%5.5d", log->line_number); > +                    "%5.5d", log_file->log->line_number); >                 ptr += 4; >             } >             if (!strncmp(ptr, "date", 4)) { > @@ -76,8 +77,8 @@ static int fwts_log_header_plaintext(fwts_log *log, >                     fwts_log_level_to_str(level)); >                 ptr += 5; >             } > -            if (!strncmp(ptr,"owner", 5) && log->owner) { > -                n += snprintf(buffer + n, len - n, "%-15.15s", log->owner); > +            if (!strncmp(ptr,"owner", 5) && log_file->log->owner) { > +                n += snprintf(buffer + n, len - n, "%-15.15s", log_file->log->owner); >                 ptr += 5; >             } >         } else { > @@ -90,19 +91,19 @@ static int fwts_log_header_plaintext(fwts_log *log, > > >  /* > - *  fwts_log_vprintf() > - *   vprintf to a log > + *  fwts_log_print() > + *   print to a log >  */ > -static int fwts_log_vprintf_plaintext(fwts_log *log, > +static int fwts_log_print_plaintext( > +    fwts_log_file *log_file, >     const fwts_log_field field, >     const fwts_log_level level, >     const char *status,   /* Ignored */ >     const char *label,    /* Ignored */ >     const char *prefix, > -    const char *fmt, > -    va_list args) > +    const char *buffer) >  { > -    char buffer[4096]; > +    char tmpbuf[8192]; >     int n = 0; >     int header_len; >     int len = 0; > @@ -115,15 +116,14 @@ static int fwts_log_vprintf_plaintext(fwts_log *log, > >     /* This is a pain, we neen to find out how big the leading log >      message is, so format one up. */ > -    n = header_len = fwts_log_header_plaintext(log, buffer, sizeof(buffer), field, level); > -    n += snprintf(buffer + n, sizeof(buffer) - n, "%s", prefix); > -    n += vsnprintf(buffer + n, sizeof(buffer) - n, fmt, args); > +    n = header_len = fwts_log_header_plaintext(log_file, tmpbuf, sizeof(tmpbuf), field, level); > +    n += snprintf(tmpbuf + n, sizeof(tmpbuf) - n, "%s%s", prefix, buffer); > >     /* Break text into multi-lines if necessary */ >     if (field & LOG_VERBATUM) > -        lines = fwts_list_from_text(buffer + header_len); > +        lines = fwts_list_from_text(tmpbuf + header_len); >     else > -        lines = fwts_format_text(buffer + header_len, log->line_width - header_len); > +        lines = fwts_format_text(tmpbuf + header_len, log_file->line_width - header_len); > >     len = n; > > @@ -133,17 +133,16 @@ static int fwts_log_vprintf_plaintext(fwts_log *log, >         if (!(field & LOG_NO_FIELDS)) { >             /* Re-format up a log heading with current line number which >              may increment with multiple line log messages */ > -            fwts_log_header_plaintext(log, buffer, sizeof(buffer), field, level); > -            fwrite(buffer, 1, header_len, log->fp); > +            fwts_log_header_plaintext(log_file, tmpbuf, sizeof(tmpbuf), field, level); > +            fwrite(tmpbuf, 1, header_len, log_file->fp); >         } > -        fwrite(text, 1, strlen(text), log->fp); > -        fwrite("\n", 1, 1, log->fp); > -        fflush(log->fp); > -        log->line_number++; > +        fwrite(text, 1, strlen(text), log_file->fp); > +        fwrite("\n", 1, 1, log_file->fp); > +        fflush(log_file->fp); >         len += strlen(text) + 1; >     } >     fwts_text_list_free(lines); > - > + >     return len; >  } > > @@ -151,11 +150,11 @@ static int fwts_log_vprintf_plaintext(fwts_log *log, >  *  fwts_log_underline_plaintext() >  *   write an underline across log, using character ch as the underline >  */ > -static void fwts_log_underline_plaintext(fwts_log *log, const int ch) > +static void fwts_log_underline_plaintext(fwts_log_file *log_file, const int ch) >  { >     int n; >     char *buffer; > -    size_t width = log->line_width + 1; > +    size_t width = log_file->line_width + 1; > >     if (!((LOG_SEPARATOR & LOG_FIELD_MASK) & fwts_log_filter)) >         return; > @@ -165,14 +164,13 @@ static void fwts_log_underline_plaintext(fwts_log *log, const int ch) >         return; /* Unlikely, and just abort */ > >     /* Write in leading optional line prefix */ > -    n = fwts_log_header_plaintext(log, buffer, width, LOG_SEPARATOR, LOG_LEVEL_NONE); > +    n = fwts_log_header_plaintext(log_file, buffer, width, LOG_SEPARATOR, LOG_LEVEL_NONE); > >     memset(buffer + n, ch, width  - n); >     buffer[width - 1] = '\n'; > > -    fwrite(buffer, 1, width, log->fp); > -    fflush(log->fp); > -    log->line_number++; > +    fwrite(buffer, 1, width, log_file->fp); > +    fflush(log_file->fp); > >     free(buffer); >  } > @@ -181,15 +179,14 @@ static void fwts_log_underline_plaintext(fwts_log *log, const int ch) >  *  fwts_log_newline_plaintext() >  *   write newline to log >  */ > -static void fwts_log_newline_plaintext(fwts_log *log) > +static void fwts_log_newline_plaintext(fwts_log_file *log_file) >  { > -    fwrite("\n", 1, 1, log->fp); > -    fflush(log->fp); > -    log->line_number++; > +    fwrite("\n", 1, 1, log_file->fp); > +    fflush(log_file->fp); >  } > >  fwts_log_ops fwts_log_plaintext_ops = { > -    .vprintf =    fwts_log_vprintf_plaintext, > +    .print =     fwts_log_print_plaintext, >     .underline =   fwts_log_underline_plaintext, >     .newline =    fwts_log_newline_plaintext >  }; > diff --git a/src/lib/src/fwts_log_xml.c b/src/lib/src/fwts_log_xml.c > index 57b530b..19e5e94 100644 > --- a/src/lib/src/fwts_log_xml.c > +++ b/src/lib/src/fwts_log_xml.c > @@ -38,19 +38,18 @@ static fwts_log_xml_stack_t xml_stack[MAX_XML_STACK]; >  static int xml_stack_index = 0; > >  /* > - *  fwts_log_vprintf_xml() > - *   vprintf to a log > + *  fwts_log_print_xml() > + *   print to a log >  */ > -static int fwts_log_vprintf_xml(fwts_log *log, > +static int fwts_log_print_xml( > +    fwts_log_file *log_file, >     const fwts_log_field field, >     const fwts_log_level level, >     const char *status, >     const char *label, >     const char *prefix, > -    const char *fmt, > -    va_list args) > +    const char *buffer) >  { > -    char buffer[4096]; >     struct tm tm; >     time_t now; >     char *str; > @@ -64,21 +63,21 @@ static int fwts_log_vprintf_xml(fwts_log *log, >     time(&now); >     localtime_r(&now, &tm); > > -    fprintf(log->fp, "%*s<logentry>\n", xml_stack_index * XML_INDENT, ""); > +    fprintf(log_file->fp, "%*s<logentry>\n", xml_stack_index * XML_INDENT, ""); > > -    fprintf(log->fp, "%*s<line_num>%d</line_num>\n", > +    fprintf(log_file->fp, "%*s<line_num>%d</line_num>\n", >         (xml_stack_index + 1) * XML_INDENT, > -        "", log->line_number); > +        "", log_file->log->line_number); > > -    fprintf(log->fp, "%*s<date>%2.2d/%2.2d/%-2.2d</date>\n", > +    fprintf(log_file->fp, "%*s<date>%2.2d/%2.2d/%-2.2d</date>\n", >         (xml_stack_index + 1) * XML_INDENT, >         "", tm.tm_mday, tm.tm_mon + 1, (tm.tm_year+1900) % 100); > > -    fprintf(log->fp, "%*s<time>%2.2d:%2.2d:%2.2d</time>\n", > +    fprintf(log_file->fp, "%*s<time>%2.2d:%2.2d:%2.2d</time>\n", >         (xml_stack_index + 1) * XML_INDENT, >         "", tm.tm_hour, tm.tm_min, tm.tm_sec); > > -    fprintf(log->fp, "%*s<field_type>%s</field_type>\n", > +    fprintf(log_file->fp, "%*s<field_type>%s</field_type>\n", >         (xml_stack_index + 1) * XML_INDENT, >         "", fwts_log_field_to_str_full(field)); > > @@ -86,26 +85,23 @@ static int fwts_log_vprintf_xml(fwts_log *log, >     if (!strcmp(str, " ")) >         str = "None"; > > -    fprintf(log->fp, "%*s<level>%s</level>\n", > +    fprintf(log_file->fp, "%*s<level>%s</level>\n", >         (xml_stack_index + 1) * XML_INDENT, "", str); > > -    fprintf(log->fp, "%*s<status>%s</status>\n", > +    fprintf(log_file->fp, "%*s<status>%s</status>\n", >         (xml_stack_index + 1) * XML_INDENT, >         "", *status ? status : "None"); > > -    fprintf(log->fp, "%*s<failure_label>%s</failure_label>\n", > +    fprintf(log_file->fp, "%*s<failure_label>%s</failure_label>\n", >         (xml_stack_index + 1) * XML_INDENT, >         "", label && *label ? label : "None"); > > -    vsnprintf(buffer, sizeof(buffer), fmt, args); > -    fprintf(log->fp, "%*s<log_text>%s</log_text>\n", > +    fprintf(log_file->fp, "%*s<log_text>%s</log_text>\n", >         (xml_stack_index + 1) * XML_INDENT, >         "", buffer); > > -    fprintf(log->fp, "%*s</logentry>\n", xml_stack_index * XML_INDENT, ""); > -    fflush(log->fp); > - > -    log->line_number++; > +    fprintf(log_file->fp, "%*s</logentry>\n", xml_stack_index * XML_INDENT, ""); > +    fflush(log_file->fp); > >     return 0; >  } > @@ -114,7 +110,7 @@ static int fwts_log_vprintf_xml(fwts_log *log, >  *  fwts_log_underline_xml() >  *   write an underline across log, using character ch as the underline >  */ > -static void fwts_log_underline_xml(fwts_log *log, const int ch) > +static void fwts_log_underline_xml(fwts_log_file *log_file, const int ch) >  { >     /* No-op for xml */ >  } > @@ -123,17 +119,17 @@ static void fwts_log_underline_xml(fwts_log *log, const int ch) >  *  fwts_log_newline() >  *   write newline to log >  */ > -static void fwts_log_newline_xml(fwts_log *log) > +static void fwts_log_newline_xml(fwts_log_file *log_file) >  { >     /* No-op for xml */ >  } > > -static void fwts_log_section_begin_xml(fwts_log *log, const char *name) > +static void fwts_log_section_begin_xml(fwts_log_file *log_file, const char *name) >  { >     xml_stack[xml_stack_index].name = name; > > -    fprintf(log->fp, "%*s<%s>\n", xml_stack_index * XML_INDENT, "", name); > -    fflush(log->fp); > +    fprintf(log_file->fp, "%*s<%s>\n", xml_stack_index * XML_INDENT, "", name); > +    fflush(log_file->fp); > >     if (xml_stack_index < MAX_XML_STACK) >         xml_stack_index++; > @@ -143,13 +139,13 @@ static void fwts_log_section_begin_xml(fwts_log *log, const char *name) >     } >  } > > -static void fwts_log_section_end_xml(fwts_log *log) > +static void fwts_log_section_end_xml(fwts_log_file *log_file) >  { >     if (xml_stack_index > 0) { >         xml_stack_index--; > -        fprintf(log->fp, "%*s</%s>\n", xml_stack_index * XML_INDENT, > +        fprintf(log_file->fp, "%*s</%s>\n", xml_stack_index * XML_INDENT, >             "", xml_stack[xml_stack_index].name); > -        fflush(log->fp); > +        fflush(log_file->fp); >     } else { >         fprintf(stderr, "xml log stack underflow.\n"); >         exit(EXIT_FAILURE); > @@ -157,26 +153,26 @@ static void fwts_log_section_end_xml(fwts_log *log) > >  } > > -static void fwts_log_open_xml(fwts_log *log) > +static void fwts_log_open_xml(fwts_log_file *log_file) >  { >     char *xml_header = "<?xml version=\"1.0\" encoding=\"UTF-8\" ?>\n"; > > -    fwrite(xml_header, 1, strlen(xml_header), log->fp); > -    fflush(log->fp); > +    fwrite(xml_header, 1, strlen(xml_header), log_file->fp); > +    fflush(log_file->fp); > > -    fwts_log_section_begin_xml(log, "fwts"); > +    fwts_log_section_begin_xml(log_file, "fwts"); >  } > > -static void fwts_log_close_xml(fwts_log *log) > +static void fwts_log_close_xml(fwts_log_file *log_file) >  { > -    fwts_log_section_end_xml(log); > +    fwts_log_section_end_xml(log_file); > > -    fwrite("\n", 1, 1, log->fp); > -    fflush(log->fp); > +    fwrite("\n", 1, 1, log_file->fp); > +    fflush(log_file->fp); >  } > >  fwts_log_ops fwts_log_xml_ops = { > -    .vprintf =    fwts_log_vprintf_xml, > +    .print =     fwts_log_print_xml, >     .underline =   fwts_log_underline_xml, >     .newline =    fwts_log_newline_xml, >     .section_begin = fwts_log_section_begin_xml, > -- > 1.7.10.4 > Acked-by: Keng-Yu Lin <kengyu@canonical.com>
On 06/11/2012 06:00 PM, Colin King wrote: > From: Colin Ian King<colin.king@canonical.com> > > Since we now can output many different log types we should also > allow for multiple log types to be written during a run. This involves > some considerable re-working of the logging engine. > > 1. The fw->log_type is now a bit map of log_types > 2. We add a list of log types to be written to fwts_log, this is a list > of fwts_log_file types. > 3. We need to re-work the log name handling so that we can open multiple > log files with different suffixes depending on the log type. > 4. To reduce the amount of vsnprintf() of the formatted log output we > now handle this in the log fwts_log_printf() and pass down the formatted > output to the different logging handlers rather than keep on re-formatting > at the lowest logging handler layer. > > There are a lot of changing is this patch. I tried to break it down, but > since there are so many interdependant changes I had to resort to one big > patch > > Signed-off-by: Colin Ian King<colin.king@canonical.com> > --- > src/lib/include/fwts_log.h | 66 +++++-- > src/lib/src/fwts_framework.c | 94 +++++----- > src/lib/src/fwts_log.c | 363 +++++++++++++++++++++++++++++--------- > src/lib/src/fwts_log_html.c | 133 +++++++------- > src/lib/src/fwts_log_json.c | 52 +++--- > src/lib/src/fwts_log_plaintext.c | 63 ++++--- > src/lib/src/fwts_log_xml.c | 72 ++++---- > 7 files changed, 527 insertions(+), 316 deletions(-) > Acked-by: Alex Hung <alex.hung@canonical.com>
diff --git a/src/lib/include/fwts_log.h b/src/lib/include/fwts_log.h index a659baa..513bf88 100644 --- a/src/lib/include/fwts_log.h +++ b/src/lib/include/fwts_log.h @@ -23,7 +23,10 @@ #include <stdio.h> #include <stdarg.h> -#define LOG_MAGIC 0xfe23ab13 +#include "fwts_list.h" + +#define LOG_MAGIC (0xfe23ab13) +#define LOG_MAX_BUF_SIZE (4096) /* Max output per log line */ typedef enum { LOG_RESULT = 0x00000001, @@ -58,31 +61,56 @@ typedef enum { LOG_LEVEL_INFO = 0x00000010, } fwts_log_level; +/* + * different types of log file + */ typedef enum { LOG_TYPE_NONE = 0x00000000, LOG_TYPE_PLAINTEXT = 0x00000001, LOG_TYPE_JSON = 0x00000002, - LOG_TYPE_XML = 0x00000003, - LOG_TYPE_HTML = 0x00000004, + LOG_TYPE_XML = 0x00000004, + LOG_TYPE_HTML = 0x00000008, } fwts_log_type; +/* + * different types of output log + */ +typedef enum { + LOG_FILENAME_TYPE_STDOUT = 0x00000001, /* log output to stdout */ + LOG_FILENAME_TYPE_STDERR = 0x00000002, /* log output to stderr */ + LOG_FILENAME_TYPE_FILE = 0x00000003, /* log output to a file */ +} fwts_log_filename_type; + +/* + * top level log descriptor + */ typedef struct log_t { - unsigned int magic; - FILE *fp; - char *owner; - int line_width; - int line_number; - struct fwts_log_ops_t *ops; + unsigned int magic; /* magic ID of the log */ + fwts_list log_files; /* list of fwts_log_file */ + int line_number; /* keeps track of the line numbering */ + char *owner; /* who is writing to this log */ } fwts_log; +/* + * info for a specific log type + */ +typedef struct { + FILE *fp; /* file descriptor for log */ + fwts_log *log; /* parent log struct */ + fwts_log_type type; /* log type */ + fwts_log_filename_type filename_type; /* log filename type */ + struct fwts_log_ops_t *ops; /* log operators */ + int line_width; /* width of log in chars */ +} fwts_log_file; + typedef struct fwts_log_ops_t { - int (*vprintf)(fwts_log *log, const fwts_log_field field, const fwts_log_level level, const char *status, const char *label, const char *prefix, const char *fmt, va_list args); - void (*underline)(fwts_log *log, int ch); - void (*newline)(fwts_log *log); - void (*section_begin)(fwts_log *, const char *tag); - void (*section_end)(fwts_log *); - void (*open)(fwts_log *); - void (*close)(fwts_log *); + int (*print)(fwts_log_file *log_file, const fwts_log_field field, const fwts_log_level level, const char *status, const char *label, const char *prefix, const char *buffer); + void (*underline)(fwts_log_file *log_file, int ch); + void (*newline)(fwts_log_file *log_file); + void (*section_begin)(fwts_log_file *log_file, const char *tag); + void (*section_end)(fwts_log_file *log_file); + void (*open)(fwts_log_file *log_file); + void (*close)(fwts_log_file *log_file); } fwts_log_ops; extern fwts_log_ops fwts_log_plaintext_ops; @@ -116,6 +144,12 @@ int fwts_log_line_number(fwts_log *log); void fwts_log_set_line_width(const int width); void fwts_log_section_begin(fwts_log *log, const char *name); void fwts_log_section_end(fwts_log *log); +fwts_log_filename_type fwts_log_get_filename_type(const char *name); + +static inline int fwts_log_type_count(fwts_log_type type) +{ + return __builtin_popcount(type); +} #define fwts_log_result(fw, fmt, args...) \ fwts_log_printf(fw->results, LOG_RESULT, LOG_LEVEL_NONE, "", "", "", fmt, ## args) diff --git a/src/lib/src/fwts_framework.c b/src/lib/src/fwts_framework.c index f808d8e..a6c2ba2 100644 --- a/src/lib/src/fwts_framework.c +++ b/src/lib/src/fwts_framework.c @@ -28,7 +28,8 @@ #include "fwts.h" -#define RESULTS_LOG "results.log" +/* Suffix ".log", ".xml", etc gets automatically appended */ +#define RESULTS_LOG "results" #define FWTS_RUN_ALL_FLAGS \ (FWTS_BATCH | \ @@ -96,34 +97,6 @@ static const char *fwts_copyright[] = { }; /* - * fwts_framework_log_suffix() - * set the log name suffix - */ -static void fwts_framework_log_suffix(fwts_framework *fw, const char *suffix) -{ - char *ptr; - char *new; - size_t len; - - /* Locate old suffix and kill it */ - ptr = rindex(fw->results_logname, '.'); - if (ptr != NULL) - *ptr = '\0'; - - /* Space for old log name sans old suffix + new suffix + '.' + '\0' */ - len = strlen(fw->results_logname) + strlen(suffix) + 2; - - if ((new = calloc(len, 1)) == NULL) { - fprintf(stderr, "Cannot allocate log name.\n"); - exit(EXIT_FAILURE); - } - - snprintf(new, len, "%s.%s", fw->results_logname, suffix); - free(fw->results_logname); - fw->results_logname = new; -} - -/* * fwts_framework_compare_priority() * used to register tests sorted on run priority */ @@ -677,7 +650,7 @@ static fwts_framework_test *fwts_framework_test_find(fwts_framework *fw, const c * fwts_framework_log() * log a test result */ -void fwts_framework_log(fwts_framework *fw, +void fwts_framework_log(fwts_framework *fw, fwts_log_field field, const char *label, fwts_log_level level, @@ -860,6 +833,39 @@ static int fwts_framework_skip_test_parse(fwts_framework *fw, const char *arg, f return FWTS_OK; } +/* + * fwts_framework_log_type_parse() + * parse optarg of comma separated log types + */ +static int fwts_framework_log_type_parse(fwts_framework *fw, const char *arg) +{ + char *str; + char *token; + char *saveptr = NULL; + + fw->log_type = 0; + + for (str = (char*)arg; (token = strtok_r(str, ",", &saveptr)) != NULL; str = NULL) { + if (!strcmp(token, "plaintext")) + fw->log_type |= LOG_TYPE_PLAINTEXT; + else if (!strcmp(token, "json")) + fw->log_type |= LOG_TYPE_JSON; + else if (!strcmp(token, "xml")) + fw->log_type |= LOG_TYPE_XML; + else if (!strcmp(token, "html")) + fw->log_type |= LOG_TYPE_HTML; + else { + fprintf(stderr, "--log-type can be plaintext, xml, html or json.\n"); + return FWTS_ERROR; + } + } + + if (!fw->log_type) + fw->log_type = LOG_TYPE_PLAINTEXT; + + return FWTS_OK; +} + int fwts_framework_options_handler(fwts_framework *fw, int argc, char * const argv[], int option_char, int long_index) { switch (option_char) { @@ -975,22 +981,8 @@ int fwts_framework_options_handler(fwts_framework *fw, int argc, char * const ar fwts_iasl_disassemble_all_to_file(fw); return FWTS_COMPLETE; case 32: /* --log-type */ - if (!strcmp(optarg, "plaintext")) { - fw->log_type = LOG_TYPE_PLAINTEXT; - fwts_framework_log_suffix(fw, "log"); - } else if (!strcmp(optarg, "json")) { - fw->log_type = LOG_TYPE_JSON; - fwts_framework_log_suffix(fw, "json"); - } else if (!strcmp(optarg, "xml")) { - fw->log_type = LOG_TYPE_XML; - fwts_framework_log_suffix(fw, "xml"); - } else if (!strcmp(optarg, "html")) { - fw->log_type = LOG_TYPE_HTML; - fwts_framework_log_suffix(fw, "html"); - } else { - fprintf(stderr, "--log-type can be either plaintext, xml, html or json.\n"); - return FWTS_ERROR; - } + fwts_framework_log_type_parse(fw, optarg); + /* FIX ME - check return */ break; } break; @@ -1136,6 +1128,16 @@ int fwts_framework_args(const int argc, char **argv) goto tidy_close; } + /* Ensure we have just one log type specified for non-filename logging */ + if (fwts_log_type_count(fw->log_type) > 1 && + fwts_log_get_filename_type(fw->results_logname) != LOG_FILENAME_TYPE_FILE) { + fprintf(stderr, + "Cannot specify more than one log type when " + "logging to stderr or stdout\n"); + ret = FWTS_ERROR; + goto tidy_close; + } + /* Results log */ if ((fw->results = fwts_log_open("fwts", fw->results_logname, diff --git a/src/lib/src/fwts_log.c b/src/lib/src/fwts_log.c index fea7c41..3ab7930 100644 --- a/src/lib/src/fwts_log.c +++ b/src/lib/src/fwts_log.c @@ -315,48 +315,125 @@ void fwts_log_set_format(const char *str) } /* - * fwts_log_vprintf() - * printf to a log + * fwts_log_type_filename_suffix() + * return a filename suffix on a given log type */ -int fwts_log_printf(fwts_log *log, - const fwts_log_field field, - const fwts_log_level level, - const char *status, - const char *label, - const char *prefix, - const char *fmt, ...) +static char *fwts_log_type_filename_suffix(fwts_log_type type) { - va_list args; - int ret; + switch (type) { + case LOG_TYPE_JSON: + return ".json"; + case LOG_TYPE_XML: + return ".xml"; + case LOG_TYPE_HTML: + return ".html"; + case LOG_TYPE_NONE: + case LOG_TYPE_PLAINTEXT: + default: + return ".log"; + } +} + +/* + * fwts_log_filename_new_suffix() + * return the log name with suffix based on log type + */ +static char *fwts_log_filename(const char *filename, fwts_log_type type) +{ + char *ptr; + char *new_name; + char *suffix; + size_t suffix_len; + size_t trunc_len; + size_t filename_len; + + suffix = fwts_log_type_filename_suffix(type); + suffix_len = strlen(suffix); + + /* + * Locate an existing suffix, if it is one we recognise + * then remove it and append the appropriate one + */ + ptr = rindex(filename, '.'); + if (ptr && + (!strcmp(ptr, ".log") || + !strcmp(ptr, ".json") || + !strcmp(ptr, ".xml") || + !strcmp(ptr, ".html"))) { + + trunc_len = ptr - filename; + if ((new_name = calloc(trunc_len + suffix_len + 1, 1)) == NULL) { + fprintf(stderr, "Cannot allocate log name.\n"); + return NULL; + } + strncpy(new_name, filename, trunc_len); + strcat(new_name, suffix); /* strcat OK because calloc zero'd all of new_name */ + return new_name; + } - va_start(args, fmt); - ret = fwts_log_vprintf(log, field, level, status, label, prefix, fmt, args); - va_end(args); + /* + * We didn't find a suffix or a known suffix, so append + * the appropriate one to the given log filename + */ + filename_len = strlen(filename); + if ((new_name = calloc(filename_len + suffix_len + 1, 1)) == NULL) { + fprintf(stderr, "Cannot allocate log name.\n"); + return NULL; + } - return ret; + strcpy(new_name, filename); + strcat(new_name, suffix); + + return new_name; } /* * fwts_log_vprintf() - * vprintf to a log + * printf to a log */ -int fwts_log_vprintf(fwts_log *log, +int fwts_log_printf(fwts_log *log, const fwts_log_field field, const fwts_log_level level, const char *status, const char *label, const char *prefix, - const char *fmt, - va_list args) + const char *fmt, ...) { + va_list args; + int ret = 0; + + char buffer[LOG_MAX_BUF_SIZE]; + if (!((field & LOG_FIELD_MASK) & fwts_log_filter)) - return 0; + return ret; + + if (log && log->magic == LOG_MAGIC) { + fwts_list_link *item; + + /* + * With the possibility of having multiple logs being written + * to per call of fwts_log_printf() it is more efficient to + * vsnprintf() here and then pass the formatted output down to + * each log handler rather than re-formatting each time in each + * handler + */ + va_start(args, fmt); + ret = vsnprintf(buffer, sizeof(buffer), fmt, args); + if (ret < 0) + return ret; + + fwts_list_foreach(item, &log->log_files) { + fwts_log_file *log_file = fwts_list_data(fwts_log_file *, item); + + if (log_file->ops && log_file->ops->print) + log_file->ops->print(log_file, field, level, + status, label, prefix, buffer); + } + log->line_number++; - if (log && log->magic == LOG_MAGIC && - log->ops && log->ops->underline) - return log->ops->vprintf(log, field, level, status, label, prefix, fmt, args); - else - return 0; + va_end(args); + } + return ret; } /* @@ -365,9 +442,16 @@ int fwts_log_vprintf(fwts_log *log, */ void fwts_log_underline(fwts_log *log, const int ch) { - if (log && log->magic == LOG_MAGIC && - log->ops && log->ops->underline) - log->ops->underline(log, ch); + if (log && log->magic == LOG_MAGIC) { + fwts_list_link *item; + + fwts_list_foreach(item, &log->log_files) { + fwts_log_file *log_file = fwts_list_data(fwts_log_file *, item); + + if (log_file->ops && log_file->ops->underline) + log_file->ops->underline(log_file, ch); + } + } } /* @@ -376,9 +460,17 @@ void fwts_log_underline(fwts_log *log, const int ch) */ void fwts_log_newline(fwts_log *log) { - if (log && log->magic == LOG_MAGIC && - log->ops && log->ops->underline) - log->ops->newline(log); + if (log && log->magic == LOG_MAGIC) { + fwts_list_link *item; + + fwts_list_foreach(item, &log->log_files) { + fwts_log_file *log_file = fwts_list_data(fwts_log_file *, item); + + if (log_file->ops && log_file->ops->newline) + log_file->ops->newline(log_file); + } + log->line_number++; + } } int fwts_log_set_owner(fwts_log *log, const char *owner) @@ -395,96 +487,199 @@ int fwts_log_set_owner(fwts_log *log, const char *owner) return FWTS_ERROR; } + +/* + * fwts_log_section_begin() + * mark a start of a named section. For structured logging + * such as XML and JSON this pushes a new named tagged section + */ void fwts_log_section_begin(fwts_log *log, const char *name) { - if (log && log->magic == LOG_MAGIC && - log->ops && log->ops->section_begin) - log->ops->section_begin(log, name); + if (log && log->magic == LOG_MAGIC) { + fwts_list_link *item; + + fwts_list_foreach(item, &log->log_files) { + fwts_log_file *log_file = fwts_list_data(fwts_log_file *, item); + + if (log_file->ops && log_file->ops->section_begin) + log_file->ops->section_begin(log_file, name); + } + } } +/* + * fwts_log_section_end() + * mark end of a named section. For structured logging + * such as XML and JSON this pops the end of a tagged section + */ void fwts_log_section_end(fwts_log *log) { - if (log && log->magic == LOG_MAGIC && - log->ops && log->ops->section_end) - log->ops->section_end(log); + if (log && log->magic == LOG_MAGIC) { + fwts_list_link *item; + + fwts_list_foreach(item, &log->log_files) { + fwts_log_file *log_file = fwts_list_data(fwts_log_file *, item); + + if (log_file->ops && log_file->ops->section_end) + log_file->ops->section_end(log_file); + } + } } /* - * fwts_log_open() - * open a log file. if name is stderr or stdout, then attach log to these - * streams. + * fwts_log_get_ops() + * return log ops basedon log type */ -fwts_log *fwts_log_open(const char *owner, const char *name, const char *mode, fwts_log_type type) +static fwts_log_ops *fwts_log_get_ops(fwts_log_type type) { - fwts_log *newlog; - - if ((newlog = calloc(1, sizeof(fwts_log))) == NULL) - return NULL; - - newlog->magic = LOG_MAGIC; switch (type) { case LOG_TYPE_JSON: - newlog->ops = &fwts_log_json_ops; - break; + return &fwts_log_json_ops; case LOG_TYPE_PLAINTEXT: - newlog->ops = &fwts_log_plaintext_ops; - break; + return &fwts_log_plaintext_ops; case LOG_TYPE_XML: - newlog->ops = &fwts_log_xml_ops; - break; + return &fwts_log_xml_ops; case LOG_TYPE_HTML: - newlog->ops = &fwts_log_html_ops; - break; + return &fwts_log_html_ops; case LOG_TYPE_NONE: default: - newlog->ops = &fwts_log_plaintext_ops; - break; + return &fwts_log_plaintext_ops; } +} - if (owner) { - if ((newlog->owner = calloc(1, strlen(owner)+1)) == NULL) { - free(newlog); - return NULL; - } - strcpy(newlog->owner, owner); - } +/* + * fwts_log_get_filename_type() + * determine the filename type + */ +fwts_log_filename_type fwts_log_get_filename_type(const char *filename) +{ + if (!strcmp(filename, "stderr")) + return LOG_FILENAME_TYPE_STDERR; + else if (!strcmp(filename, "stdout")) + return LOG_FILENAME_TYPE_STDOUT; + else + return LOG_FILENAME_TYPE_FILE; +} + +/* + * fwts_log_open() + * open a log file. if name is stderr or stdout, then attach log to these + * streams. + */ +fwts_log *fwts_log_open( + const char *owner, /* Creator of the log */ + const char *filename, /* Log file name */ + const char *mode, /* open mode, see fopen() modes */ + fwts_log_type type) /* Log type */ +{ + fwts_log *newlog; + unsigned int i; + char *newname; - if (strcmp("stderr", name) == 0) - newlog->fp = stderr; - else if (strcmp("stdout", name) == 0) - newlog->fp = stdout; - else if ((newlog->fp = fopen(name, mode)) == NULL) { - free(newlog); + if ((newlog = calloc(1, sizeof(fwts_log))) == NULL) return NULL; - } - if (log_line_width) { - /* User has specified width, so use it */ - newlog->line_width = log_line_width; - } else { - newlog->line_width = fwts_tty_width(fileno(newlog->fp), LOG_LINE_WIDTH); - } + newlog->magic = LOG_MAGIC; - if (newlog->ops && newlog->ops->open) - newlog->ops->open(newlog); + fwts_log_set_owner(newlog, owner); + fwts_list_init(&newlog->log_files); + + /* + * Scan through and see which log types have been specified + * and open the log file with the appropriate ops to perform + * the logging + */ + for (i=0; i<32; i++) { + fwts_log_type mask = 1 << i; /* The log type for this iteration */ + + /* If set then go and open up a log for this log type */ + if (type & mask) { + fwts_log_file *log_file; + + if ((log_file = calloc(1, sizeof(fwts_log_file))) == NULL) { + fwts_log_close(newlog); + return NULL; + } + + log_file->type = mask; + log_file->ops = fwts_log_get_ops(mask); + log_file->log = newlog; + log_file->filename_type = fwts_log_get_filename_type(filename); + + /* + * To complicate matters we can have logs being + * written to stderr, stdout or two a named file + */ + switch(log_file->filename_type) { + case LOG_FILENAME_TYPE_STDERR: + log_file->fp = stderr; + break; + case LOG_FILENAME_TYPE_STDOUT: + log_file->fp = stdout; + break; + case LOG_FILENAME_TYPE_FILE: + if ((newname = fwts_log_filename(filename, mask)) == NULL) { + fwts_log_close(newlog); + return NULL; + } + log_file->fp = fopen(newname, mode); + free(newname); + + if (log_file->fp == NULL) { + fwts_log_close(newlog); + return NULL; + } + } + + /* Fix up the log specific line width */ + if (log_line_width) { + /* User has specified width, so use it */ + log_file->line_width = log_line_width; + } else { + log_file->line_width = + fwts_tty_width(fileno(log_file->fp), LOG_LINE_WIDTH); + } + + /* ..and add the log file to the list of logs */ + fwts_list_append(&newlog->log_files, log_file); + + /* ..and do the log specific opening set up */ + if (log_file->ops && log_file->ops->open) + log_file->ops->open(log_file); + } + } return newlog; } /* * fwts_log_close() - * close a log file + * close any opened log files, free up memory */ int fwts_log_close(fwts_log *log) { if (log && (log->magic == LOG_MAGIC)) { - if (log->ops && log->ops->close) - log->ops->close(log); + fwts_list_link *item; + + fwts_list_foreach(item, &log->log_files) { + fwts_log_file *log_file = fwts_list_data(fwts_log_file *, item); + + /* Do the log type specific close */ + if (log_file->ops && log_file->ops->close) + log_file->ops->close(log_file); + + /* Close opened log file */ + if (log_file->fp && + log_file->filename_type == LOG_FILENAME_TYPE_FILE) + fclose(log_file->fp); + } + + /* ..and free log files */ + fwts_list_free_items(&log->log_files, free); - if (log->fp && (log->fp != stdout && log->fp != stderr)) - fclose(log->fp); if (log->owner) free(log->owner); + free(log); } return FWTS_OK; diff --git a/src/lib/src/fwts_log_html.c b/src/lib/src/fwts_log_html.c index a70ac21..1ca79b8 100644 --- a/src/lib/src/fwts_log_html.c +++ b/src/lib/src/fwts_log_html.c @@ -37,31 +37,31 @@ typedef struct { static fwts_log_html_stack_t html_stack[MAX_HTML_STACK]; static int html_stack_index = 0; -static void fwts_log_html(fwts_log *log, const char *fmt, ...) +static void fwts_log_html(fwts_log_file *log_file, const char *fmt, ...) { va_list args; va_start(args, fmt); - fprintf(log->fp, "%*s", html_stack_index * HTML_INDENT, ""); - vfprintf(log->fp, fmt, args); + fprintf(log_file->fp, "%*s", html_stack_index * HTML_INDENT, ""); + vfprintf(log_file->fp, fmt, args); va_end(args); } /* - * fwts_log_vprintf_html() - * vprintf to a log + * fwts_log_print_html() + * print to a log */ -static int fwts_log_vprintf_html(fwts_log *log, +static int fwts_log_print_html( + fwts_log_file *log_file, const fwts_log_field field, const fwts_log_level level, const char *status, const char *label, const char *prefix, - const char *fmt, - va_list args) + const char *buffer) { char *str; char *style; @@ -74,7 +74,7 @@ static int fwts_log_vprintf_html(fwts_log *log, if (field & (LOG_NEWLINE | LOG_SEPARATOR | LOG_DEBUG)) return 0; - fwts_log_html(log, "<TR>\n"); + fwts_log_html(log_file, "<TR>\n"); if (field & LOG_VERBATUM) { code_start = "<PRE class=style_code>"; @@ -86,29 +86,24 @@ static int fwts_log_vprintf_html(fwts_log *log, switch (field & LOG_FIELD_MASK) { case LOG_ERROR: - fwts_log_html(log, " <TD class=style_error>Error</TD><TD COLSPAN=2>"); - vfprintf(log->fp, fmt, args); - fprintf(log->fp, "</TD>\n"); + fwts_log_html(log_file, " <TD class=style_error>Error</TD>" + "<TD COLSPAN=2>%s</TD>\n", buffer); break; case LOG_WARNING: - fwts_log_html(log, " <TD class=style_error>Warning</TD><TD COLSPAN=2 class=style_advice_info>%s", code_start); - vfprintf(log->fp, fmt, args); - fprintf(log->fp, "%s</TD>\n", code_end); + fwts_log_html(log_file, " <TD class=style_error>Warning</TD>" + "<TD COLSPAN=2 class=style_advice_info>%s%s%s</TD>\n", + code_start, buffer, code_end); break; case LOG_HEADING: - fwts_log_html(log, "<TD COLSPAN=2 class=style_heading>%s", code_start); - vfprintf(log->fp, fmt, args); - fprintf(log->fp, "%s</TD>\n", code_end); + fwts_log_html(log_file, "<TD COLSPAN=2 class=style_heading>%s%s%s</TD>\n", + code_start, buffer, code_end); break; case LOG_INFO: - fwts_log_html(log, " <TD></TD><TD COLSPAN=2 class=style_infos>%s", code_start); - vfprintf(log->fp, fmt, args); - fprintf(log->fp, "%s</TD>\n", code_end); + fwts_log_html(log_file, " <TD></TD><TD COLSPAN=2 class=style_infos>%s%s%s</TD>\n", + code_start, buffer, code_end); break; case LOG_PASSED: - fwts_log_html(log, "<TD class=style_passed>PASSED</TD><TD>"); - vfprintf(log->fp, fmt, args); - fprintf(log->fp, "</TD>\n"); + fwts_log_html(log_file, "<TD class=style_passed>PASSED</TD><TD>%s</TD>\n", buffer); break; case LOG_FAILED: switch (level) { @@ -132,39 +127,33 @@ static int fwts_log_vprintf_html(fwts_log *log, } str = fwts_log_level_to_str(level); - fwts_log_html(log, " <TD%s>%s [%s]</TD>\n", style, *status ? status : "", str); - - fwts_log_html(log, " <TD>"); - vfprintf(log->fp, fmt, args); - fprintf(log->fp, "</TD>\n"); + fwts_log_html(log_file, " <TD%s>%s [%s]</TD>\n", style, *status ? status : "", str); + fwts_log_html(log_file, " <TD>%s</TD>\n", buffer); break; case LOG_SKIPPED: - fwts_log_html(log, "<TD class=style_skipped>Skipped</TD><TD>%s", code_start); - vfprintf(log->fp, fmt, args); - fprintf(log->fp, "%s</TD>\n", code_end); + fwts_log_html(log_file, "<TD class=style_skipped>Skipped</TD>" + "<TD>%s%s%s</TD>\n", code_start, buffer, code_end); break; case LOG_SUMMARY: - fwts_log_html(log, " <TD></TD><TD COLSPAN=2 class=style_summary>%s", code_start); - vfprintf(log->fp, fmt, args); - fprintf(log->fp, "%s</TD>\n", code_end); + fwts_log_html(log_file, " <TD></TD>" + "<TD COLSPAN=2 class=style_summary>%s%s%s</TD>\n", + code_start, buffer, code_end); break; case LOG_ADVICE: - fwts_log_html(log, " <TD class=style_advice>Advice</TD><TD COLSPAN=2 class=style_advice_info>%s", code_start); - vfprintf(log->fp, fmt, args); - fprintf(log->fp, "%s</TD>\n", code_end); + fwts_log_html(log_file, " <TD class=style_advice>Advice</TD>" + "<TD COLSPAN=2 class=style_advice_info>%s%s%s</TD>\n", + code_start, buffer, code_end); break; default: break; } - fwts_log_html(log, "</TR>\n"); - fflush(log->fp); - - log->line_number++; + fwts_log_html(log_file, "</TR>\n"); + fflush(log_file->fp); return 0; } @@ -173,7 +162,7 @@ static int fwts_log_vprintf_html(fwts_log *log, * fwts_log_underline_html() * write an underline across log, using character ch as the underline */ -static void fwts_log_underline_html(fwts_log *log, const int ch) +static void fwts_log_underline_html(fwts_log_file *log_file, const int ch) { /* No-op for html */ } @@ -182,26 +171,26 @@ static void fwts_log_underline_html(fwts_log *log, const int ch) * fwts_log_newline() * write newline to log */ -static void fwts_log_newline_html(fwts_log *log) +static void fwts_log_newline_html(fwts_log_file *log_file) { /* No-op for html */ } -static void fwts_log_section_begin_html(fwts_log *log, const char *name) +static void fwts_log_section_begin_html(fwts_log_file *log_file, const char *name) { html_stack[html_stack_index].name = name; if (!strcmp(name, "summary")) { - fwts_log_html(log, "<TR><TD class=style_heading COLSPAN=2>Summary</TD></TR>\n"); + fwts_log_html(log_file, "<TR><TD class=style_heading COLSPAN=2>Summary</TD></TR>\n"); } else if (!strcmp(name, "heading")) { - fwts_log_html(log, "<TR><TD class=style_heading COLSPAN=2>Firmware Test Suite</TD></TR>\n"); + fwts_log_html(log_file, "<TR><TD class=style_heading COLSPAN=2>Firmware Test Suite</TD></TR>\n"); } else if (!strcmp(name, "subtest_info")) { - fwts_log_html(log, "<TR><TD class=style_subtest COLSPAN=2></TD></TR>\n"); + fwts_log_html(log_file, "<TR><TD class=style_subtest COLSPAN=2></TD></TR>\n"); } else if (!strcmp(name, "failure")) { - fwts_log_html(log, "<TR><TD class=style_heading COLSPAN=2></TD></TR>\n"); + fwts_log_html(log_file, "<TR><TD class=style_heading COLSPAN=2></TD></TR>\n"); } - fflush(log->fp); + fflush(log_file->fp); if (html_stack_index < MAX_HTML_STACK) html_stack_index++; @@ -211,11 +200,11 @@ static void fwts_log_section_begin_html(fwts_log *log, const char *name) } } -static void fwts_log_section_end_html(fwts_log *log) +static void fwts_log_section_end_html(fwts_log_file *log_file) { if (html_stack_index > 0) { html_stack_index--; - fflush(log->fp); + fflush(log_file->fp); } else { fprintf(stderr, "html log stack underflow.\n"); exit(EXIT_FAILURE); @@ -223,15 +212,15 @@ static void fwts_log_section_end_html(fwts_log *log) } -static void fwts_log_open_html(fwts_log *log) +static void fwts_log_open_html(fwts_log_file *log_file) { - fwts_log_html(log, "<HTML>\n"); - fwts_log_html(log, "<HEAD>\n"); - fwts_log_html(log, " <TITLE>fwts log</TITLE>\n"); - fwts_log_html(log, "</HEAD>\n"); - fwts_log_html(log, "<BODY>\n"); - fwts_log_html(log, "<STYLE>\n"); - fwts_log_html(log, + fwts_log_html(log_file, "<HTML>\n"); + fwts_log_html(log_file, "<HEAD>\n"); + fwts_log_html(log_file, " <TITLE>fwts log</TITLE>\n"); + fwts_log_html(log_file, "</HEAD>\n"); + fwts_log_html(log_file, "<BODY>\n"); + fwts_log_html(log_file, "<STYLE>\n"); + fwts_log_html(log_file, ".style_critical { background-color: red; font-weight: bold; " "text-align: center; vertical-align: center }\n" ".style_high { background-color: orange; font-weight: bold; " @@ -256,27 +245,27 @@ static void fwts_log_open_html(fwts_log *log) ".style_info { }\n" ".style_code { font-family: \"courier\",\"mono\"; font-size:0.75em; overflow:auto; " "width:90%; line-height:0.82em; font-stretch:extra-condensed; word-wrap:normal }\n"); - fwts_log_html(log, "</STYLE>\n"); - fflush(log->fp); + fwts_log_html(log_file, "</STYLE>\n"); + fflush(log_file->fp); - fwts_log_html(log, "<TABLE WIDTH=1024>\n"); - fwts_log_html(log, "</TR>\n"); + fwts_log_html(log_file, "<TABLE WIDTH=1024>\n"); + fwts_log_html(log_file, "</TR>\n"); - fwts_log_section_begin_html(log, "fwts"); + fwts_log_section_begin_html(log_file, "fwts"); } -static void fwts_log_close_html(fwts_log *log) +static void fwts_log_close_html(fwts_log_file *log_file) { - fwts_log_section_end_html(log); + fwts_log_section_end_html(log_file); - fwts_log_html(log, "</TABLE>\n"); - fwts_log_html(log, "</BODY>\n"); - fwts_log_html(log, "</HTML>\n"); - fflush(log->fp); + fwts_log_html(log_file, "</TABLE>\n"); + fwts_log_html(log_file, "</BODY>\n"); + fwts_log_html(log_file, "</HTML>\n"); + fflush(log_file->fp); } fwts_log_ops fwts_log_html_ops = { - .vprintf = fwts_log_vprintf_html, + .print = fwts_log_print_html, .underline = fwts_log_underline_html, .newline = fwts_log_newline_html, .section_begin = fwts_log_section_begin_html, diff --git a/src/lib/src/fwts_log_json.c b/src/lib/src/fwts_log_json.c index 208847c..8dd65e4 100644 --- a/src/lib/src/fwts_log_json.c +++ b/src/lib/src/fwts_log_json.c @@ -40,19 +40,19 @@ static fwts_log_json_stack_t json_stack[MAX_JSON_STACK]; static int json_stack_index = 0; /* - * fwts_log_vprintf_json() - * vprintf to a log + * fwts_log_printf_son() + * print to a log */ -static int fwts_log_vprintf_json(fwts_log *log, +static int fwts_log_print_json( + fwts_log_file *log_file, const fwts_log_field field, const fwts_log_level level, const char *status, const char *label, const char *prefix, - const char *fmt, - va_list args) + const char *buffer) { - char buffer[4096]; + char tmpbuf[4096]; struct tm tm; time_t now; json_object *header; @@ -69,13 +69,13 @@ static int fwts_log_vprintf_json(fwts_log *log, localtime_r(&now, &tm); header = json_object_new_object(); - json_object_object_add(header, "line_num", json_object_new_int(log->line_number)); - snprintf(buffer, sizeof(buffer), "%2.2d/%2.2d/%-2.2d", + json_object_object_add(header, "line_num", json_object_new_int(log_file->log->line_number)); + snprintf(tmpbuf, sizeof(tmpbuf), "%2.2d/%2.2d/%-2.2d", tm.tm_mday, tm.tm_mon + 1, (tm.tm_year+1900) % 100); - json_object_object_add(header, "date", json_object_new_string(buffer)); - snprintf(buffer, sizeof(buffer), "%2.2d:%2.2d:%2.2d", + json_object_object_add(header, "date", json_object_new_string(tmpbuf)); + snprintf(tmpbuf, sizeof(tmpbuf), "%2.2d:%2.2d:%2.2d", tm.tm_hour, tm.tm_min, tm.tm_sec); - json_object_object_add(header, "time", json_object_new_string(buffer)); + json_object_object_add(header, "time", json_object_new_string(tmpbuf)); json_object_object_add(header, "field_type", json_object_new_string(fwts_log_field_to_str_full(field))); @@ -92,13 +92,10 @@ static int fwts_log_vprintf_json(fwts_log *log, json_object_object_add(header, "owner", json_object_new_string(log->owner)); */ - vsnprintf(buffer, sizeof(buffer), fmt, args); json_object_object_add(header, "log_text", json_object_new_string(buffer)); json_object_array_add(json_log, header); - log->line_number++; - return 0; } @@ -106,7 +103,7 @@ static int fwts_log_vprintf_json(fwts_log *log, * fwts_log_underline_json() * write an underline across log, using character ch as the underline */ -static void fwts_log_underline_json(fwts_log *log, const int ch) +static void fwts_log_underline_json(fwts_log_file *log_file, const int ch) { /* No-op for json */ } @@ -115,12 +112,12 @@ static void fwts_log_underline_json(fwts_log *log, const int ch) * fwts_log_newline() * write newline to log */ -static void fwts_log_newline_json(fwts_log *log) +static void fwts_log_newline_json(fwts_log_file *log_file) { /* No-op for json */ } -static void fwts_log_section_begin_json(fwts_log *log, const char *name) +static void fwts_log_section_begin_json(fwts_log_file *log_file, const char *name) { json_object *json_obj; json_object *json_log; @@ -132,7 +129,7 @@ static void fwts_log_section_begin_json(fwts_log *log, const char *name) json_stack[json_stack_index].obj = json_obj; json_stack[json_stack_index].log = json_log; - if (json_stack_index > 0) + if (json_stack_index > 0) json_object_array_add(json_stack[json_stack_index-1].log, json_obj); if (json_stack_index < MAX_JSON_STACK) @@ -143,7 +140,7 @@ static void fwts_log_section_begin_json(fwts_log *log, const char *name) } } -static void fwts_log_section_end_json(fwts_log *log) +static void fwts_log_section_end_json(fwts_log_file *log_file) { if (json_stack_index > 0) json_stack_index--; @@ -153,29 +150,30 @@ static void fwts_log_section_end_json(fwts_log *log) } } -static void fwts_log_open_json(fwts_log *log) +static void fwts_log_open_json(fwts_log_file *log_file) { - fwts_log_section_begin_json(log, "fwts"); + fwts_log_section_begin_json(log_file, "fwts"); } -static void fwts_log_close_json(fwts_log *log) +static void fwts_log_close_json(fwts_log_file *log_file) { const char *str; size_t len; - fwts_log_section_end_json(log); + fwts_log_section_end_json(log_file); str = json_object_to_json_string(json_stack[0].obj); len = strlen(str); - fwrite(str, 1, len, log->fp); - fwrite("\n", 1, 1, log->fp); - fflush(log->fp); + fwrite(str, 1, len, log_file->fp); + fwrite("\n", 1, 1, log_file->fp); + fflush(log_file->fp); + json_object_put(json_stack[0].obj); } fwts_log_ops fwts_log_json_ops = { - .vprintf = fwts_log_vprintf_json, + .print = fwts_log_print_json, .underline = fwts_log_underline_json, .newline = fwts_log_newline_json, .section_begin = fwts_log_section_begin_json, diff --git a/src/lib/src/fwts_log_plaintext.c b/src/lib/src/fwts_log_plaintext.c index 44c443f..7381ae3 100644 --- a/src/lib/src/fwts_log_plaintext.c +++ b/src/lib/src/fwts_log_plaintext.c @@ -32,7 +32,8 @@ * fwts_log_header_plaintext() * format up a tabulated log heading */ -static int fwts_log_header_plaintext(fwts_log *log, +static int fwts_log_header_plaintext( + fwts_log_file *log_file, char *buffer, const int len, const fwts_log_field field, @@ -51,7 +52,7 @@ static int fwts_log_header_plaintext(fwts_log *log, ptr++; if (!strncmp(ptr, "line", 4)) { n += snprintf(buffer + n, len - n, - "%5.5d", log->line_number); + "%5.5d", log_file->log->line_number); ptr += 4; } if (!strncmp(ptr, "date", 4)) { @@ -76,8 +77,8 @@ static int fwts_log_header_plaintext(fwts_log *log, fwts_log_level_to_str(level)); ptr += 5; } - if (!strncmp(ptr,"owner", 5) && log->owner) { - n += snprintf(buffer + n, len - n, "%-15.15s", log->owner); + if (!strncmp(ptr,"owner", 5) && log_file->log->owner) { + n += snprintf(buffer + n, len - n, "%-15.15s", log_file->log->owner); ptr += 5; } } else { @@ -90,19 +91,19 @@ static int fwts_log_header_plaintext(fwts_log *log, /* - * fwts_log_vprintf() - * vprintf to a log + * fwts_log_print() + * print to a log */ -static int fwts_log_vprintf_plaintext(fwts_log *log, +static int fwts_log_print_plaintext( + fwts_log_file *log_file, const fwts_log_field field, const fwts_log_level level, const char *status, /* Ignored */ const char *label, /* Ignored */ const char *prefix, - const char *fmt, - va_list args) + const char *buffer) { - char buffer[4096]; + char tmpbuf[8192]; int n = 0; int header_len; int len = 0; @@ -115,15 +116,14 @@ static int fwts_log_vprintf_plaintext(fwts_log *log, /* This is a pain, we neen to find out how big the leading log message is, so format one up. */ - n = header_len = fwts_log_header_plaintext(log, buffer, sizeof(buffer), field, level); - n += snprintf(buffer + n, sizeof(buffer) - n, "%s", prefix); - n += vsnprintf(buffer + n, sizeof(buffer) - n, fmt, args); + n = header_len = fwts_log_header_plaintext(log_file, tmpbuf, sizeof(tmpbuf), field, level); + n += snprintf(tmpbuf + n, sizeof(tmpbuf) - n, "%s%s", prefix, buffer); /* Break text into multi-lines if necessary */ if (field & LOG_VERBATUM) - lines = fwts_list_from_text(buffer + header_len); + lines = fwts_list_from_text(tmpbuf + header_len); else - lines = fwts_format_text(buffer + header_len, log->line_width - header_len); + lines = fwts_format_text(tmpbuf + header_len, log_file->line_width - header_len); len = n; @@ -133,17 +133,16 @@ static int fwts_log_vprintf_plaintext(fwts_log *log, if (!(field & LOG_NO_FIELDS)) { /* Re-format up a log heading with current line number which may increment with multiple line log messages */ - fwts_log_header_plaintext(log, buffer, sizeof(buffer), field, level); - fwrite(buffer, 1, header_len, log->fp); + fwts_log_header_plaintext(log_file, tmpbuf, sizeof(tmpbuf), field, level); + fwrite(tmpbuf, 1, header_len, log_file->fp); } - fwrite(text, 1, strlen(text), log->fp); - fwrite("\n", 1, 1, log->fp); - fflush(log->fp); - log->line_number++; + fwrite(text, 1, strlen(text), log_file->fp); + fwrite("\n", 1, 1, log_file->fp); + fflush(log_file->fp); len += strlen(text) + 1; } fwts_text_list_free(lines); - + return len; } @@ -151,11 +150,11 @@ static int fwts_log_vprintf_plaintext(fwts_log *log, * fwts_log_underline_plaintext() * write an underline across log, using character ch as the underline */ -static void fwts_log_underline_plaintext(fwts_log *log, const int ch) +static void fwts_log_underline_plaintext(fwts_log_file *log_file, const int ch) { int n; char *buffer; - size_t width = log->line_width + 1; + size_t width = log_file->line_width + 1; if (!((LOG_SEPARATOR & LOG_FIELD_MASK) & fwts_log_filter)) return; @@ -165,14 +164,13 @@ static void fwts_log_underline_plaintext(fwts_log *log, const int ch) return; /* Unlikely, and just abort */ /* Write in leading optional line prefix */ - n = fwts_log_header_plaintext(log, buffer, width, LOG_SEPARATOR, LOG_LEVEL_NONE); + n = fwts_log_header_plaintext(log_file, buffer, width, LOG_SEPARATOR, LOG_LEVEL_NONE); memset(buffer + n, ch, width - n); buffer[width - 1] = '\n'; - fwrite(buffer, 1, width, log->fp); - fflush(log->fp); - log->line_number++; + fwrite(buffer, 1, width, log_file->fp); + fflush(log_file->fp); free(buffer); } @@ -181,15 +179,14 @@ static void fwts_log_underline_plaintext(fwts_log *log, const int ch) * fwts_log_newline_plaintext() * write newline to log */ -static void fwts_log_newline_plaintext(fwts_log *log) +static void fwts_log_newline_plaintext(fwts_log_file *log_file) { - fwrite("\n", 1, 1, log->fp); - fflush(log->fp); - log->line_number++; + fwrite("\n", 1, 1, log_file->fp); + fflush(log_file->fp); } fwts_log_ops fwts_log_plaintext_ops = { - .vprintf = fwts_log_vprintf_plaintext, + .print = fwts_log_print_plaintext, .underline = fwts_log_underline_plaintext, .newline = fwts_log_newline_plaintext }; diff --git a/src/lib/src/fwts_log_xml.c b/src/lib/src/fwts_log_xml.c index 57b530b..19e5e94 100644 --- a/src/lib/src/fwts_log_xml.c +++ b/src/lib/src/fwts_log_xml.c @@ -38,19 +38,18 @@ static fwts_log_xml_stack_t xml_stack[MAX_XML_STACK]; static int xml_stack_index = 0; /* - * fwts_log_vprintf_xml() - * vprintf to a log + * fwts_log_print_xml() + * print to a log */ -static int fwts_log_vprintf_xml(fwts_log *log, +static int fwts_log_print_xml( + fwts_log_file *log_file, const fwts_log_field field, const fwts_log_level level, const char *status, const char *label, const char *prefix, - const char *fmt, - va_list args) + const char *buffer) { - char buffer[4096]; struct tm tm; time_t now; char *str; @@ -64,21 +63,21 @@ static int fwts_log_vprintf_xml(fwts_log *log, time(&now); localtime_r(&now, &tm); - fprintf(log->fp, "%*s<logentry>\n", xml_stack_index * XML_INDENT, ""); + fprintf(log_file->fp, "%*s<logentry>\n", xml_stack_index * XML_INDENT, ""); - fprintf(log->fp, "%*s<line_num>%d</line_num>\n", + fprintf(log_file->fp, "%*s<line_num>%d</line_num>\n", (xml_stack_index + 1) * XML_INDENT, - "", log->line_number); + "", log_file->log->line_number); - fprintf(log->fp, "%*s<date>%2.2d/%2.2d/%-2.2d</date>\n", + fprintf(log_file->fp, "%*s<date>%2.2d/%2.2d/%-2.2d</date>\n", (xml_stack_index + 1) * XML_INDENT, "", tm.tm_mday, tm.tm_mon + 1, (tm.tm_year+1900) % 100); - fprintf(log->fp, "%*s<time>%2.2d:%2.2d:%2.2d</time>\n", + fprintf(log_file->fp, "%*s<time>%2.2d:%2.2d:%2.2d</time>\n", (xml_stack_index + 1) * XML_INDENT, "", tm.tm_hour, tm.tm_min, tm.tm_sec); - fprintf(log->fp, "%*s<field_type>%s</field_type>\n", + fprintf(log_file->fp, "%*s<field_type>%s</field_type>\n", (xml_stack_index + 1) * XML_INDENT, "", fwts_log_field_to_str_full(field)); @@ -86,26 +85,23 @@ static int fwts_log_vprintf_xml(fwts_log *log, if (!strcmp(str, " ")) str = "None"; - fprintf(log->fp, "%*s<level>%s</level>\n", + fprintf(log_file->fp, "%*s<level>%s</level>\n", (xml_stack_index + 1) * XML_INDENT, "", str); - fprintf(log->fp, "%*s<status>%s</status>\n", + fprintf(log_file->fp, "%*s<status>%s</status>\n", (xml_stack_index + 1) * XML_INDENT, "", *status ? status : "None"); - fprintf(log->fp, "%*s<failure_label>%s</failure_label>\n", + fprintf(log_file->fp, "%*s<failure_label>%s</failure_label>\n", (xml_stack_index + 1) * XML_INDENT, "", label && *label ? label : "None"); - vsnprintf(buffer, sizeof(buffer), fmt, args); - fprintf(log->fp, "%*s<log_text>%s</log_text>\n", + fprintf(log_file->fp, "%*s<log_text>%s</log_text>\n", (xml_stack_index + 1) * XML_INDENT, "", buffer); - fprintf(log->fp, "%*s</logentry>\n", xml_stack_index * XML_INDENT, ""); - fflush(log->fp); - - log->line_number++; + fprintf(log_file->fp, "%*s</logentry>\n", xml_stack_index * XML_INDENT, ""); + fflush(log_file->fp); return 0; } @@ -114,7 +110,7 @@ static int fwts_log_vprintf_xml(fwts_log *log, * fwts_log_underline_xml() * write an underline across log, using character ch as the underline */ -static void fwts_log_underline_xml(fwts_log *log, const int ch) +static void fwts_log_underline_xml(fwts_log_file *log_file, const int ch) { /* No-op for xml */ } @@ -123,17 +119,17 @@ static void fwts_log_underline_xml(fwts_log *log, const int ch) * fwts_log_newline() * write newline to log */ -static void fwts_log_newline_xml(fwts_log *log) +static void fwts_log_newline_xml(fwts_log_file *log_file) { /* No-op for xml */ } -static void fwts_log_section_begin_xml(fwts_log *log, const char *name) +static void fwts_log_section_begin_xml(fwts_log_file *log_file, const char *name) { xml_stack[xml_stack_index].name = name; - fprintf(log->fp, "%*s<%s>\n", xml_stack_index * XML_INDENT, "", name); - fflush(log->fp); + fprintf(log_file->fp, "%*s<%s>\n", xml_stack_index * XML_INDENT, "", name); + fflush(log_file->fp); if (xml_stack_index < MAX_XML_STACK) xml_stack_index++; @@ -143,13 +139,13 @@ static void fwts_log_section_begin_xml(fwts_log *log, const char *name) } } -static void fwts_log_section_end_xml(fwts_log *log) +static void fwts_log_section_end_xml(fwts_log_file *log_file) { if (xml_stack_index > 0) { xml_stack_index--; - fprintf(log->fp, "%*s</%s>\n", xml_stack_index * XML_INDENT, + fprintf(log_file->fp, "%*s</%s>\n", xml_stack_index * XML_INDENT, "", xml_stack[xml_stack_index].name); - fflush(log->fp); + fflush(log_file->fp); } else { fprintf(stderr, "xml log stack underflow.\n"); exit(EXIT_FAILURE); @@ -157,26 +153,26 @@ static void fwts_log_section_end_xml(fwts_log *log) } -static void fwts_log_open_xml(fwts_log *log) +static void fwts_log_open_xml(fwts_log_file *log_file) { char *xml_header = "<?xml version=\"1.0\" encoding=\"UTF-8\" ?>\n"; - fwrite(xml_header, 1, strlen(xml_header), log->fp); - fflush(log->fp); + fwrite(xml_header, 1, strlen(xml_header), log_file->fp); + fflush(log_file->fp); - fwts_log_section_begin_xml(log, "fwts"); + fwts_log_section_begin_xml(log_file, "fwts"); } -static void fwts_log_close_xml(fwts_log *log) +static void fwts_log_close_xml(fwts_log_file *log_file) { - fwts_log_section_end_xml(log); + fwts_log_section_end_xml(log_file); - fwrite("\n", 1, 1, log->fp); - fflush(log->fp); + fwrite("\n", 1, 1, log_file->fp); + fflush(log_file->fp); } fwts_log_ops fwts_log_xml_ops = { - .vprintf = fwts_log_vprintf_xml, + .print = fwts_log_print_xml, .underline = fwts_log_underline_xml, .newline = fwts_log_newline_xml, .section_begin = fwts_log_section_begin_xml,