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

Chris Van Hoof vanhoof at canonical.com
Mon Jun 11 15:53:36 UTC 2012


On 06/11/2012 06:00 AM, Colin King 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,

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 at canonical.com>




More information about the fwts-devel mailing list