[PATCH 1/3] lib: rework logging and framework to allow for multiple output logs

Keng-Yu Lin kengyu at canonical.com
Wed Jun 13 09:49:07 UTC 2012


On Mon, Jun 11, 2012 at 6:00 PM, Colin King <colin.king at canonical.com> wrote:
> From: Colin Ian King <colin.king at 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 at 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 at canonical.com>


More information about the fwts-devel mailing list