Patchwork [1/2] lib: add xml logging

login
register
mail settings
Submitter Colin King
Date May 28, 2012, 3:01 p.m.
Message ID <1338217284-8752-1-git-send-email-colin.king@canonical.com>
Download mbox | patch
Permalink /patch/161625/
State Accepted
Headers show

Comments

Colin King - May 28, 2012, 3:01 p.m.
From: Colin Ian King <colin.king@canonical.com>

Signed-off-by: Colin Ian King <colin.king@canonical.com>
---
 doc/fwts.1                   |    2 +-
 src/lib/include/fwts_log.h   |    1 +
 src/lib/src/Makefile.am      |    1 +
 src/lib/src/fwts_framework.c |    4 +-
 src/lib/src/fwts_log.c       |    3 +
 src/lib/src/fwts_log_xml.c   |  186 ++++++++++++++++++++++++++++++++++++++++++
 6 files changed, 195 insertions(+), 2 deletions(-)
 create mode 100644 src/lib/src/fwts_log_xml.c
Keng-Yu Lin - May 31, 2012, 6:54 a.m.
On Mon, May 28, 2012 at 11:01 PM, Colin King <colin.king@canonical.com> wrote:
> From: Colin Ian King <colin.king@canonical.com>
>
> Signed-off-by: Colin Ian King <colin.king@canonical.com>
> ---
>  doc/fwts.1                   |    2 +-
>  src/lib/include/fwts_log.h   |    1 +
>  src/lib/src/Makefile.am      |    1 +
>  src/lib/src/fwts_framework.c |    4 +-
>  src/lib/src/fwts_log.c       |    3 +
>  src/lib/src/fwts_log_xml.c   |  186 ++++++++++++++++++++++++++++++++++++++++++
>  6 files changed, 195 insertions(+), 2 deletions(-)
>  create mode 100644 src/lib/src/fwts_log_xml.c
>
> diff --git a/doc/fwts.1 b/doc/fwts.1
> index 0d773cd..09eee56 100644
> --- a/doc/fwts.1
> +++ b/doc/fwts.1
> @@ -150,7 +150,7 @@ specify the information in each log line. The following specifiers are available
>  e.g. \-\-log\-format="%date %time [%field] (%owner): "
>  .TP
>  .B \-\-log\-type
> -specify the log type. Currently plaintext and json log types are available and the
> +specify the log type. Currently plaintext, json and xml log types are available and the
>  default is plaintext.
>  .TP
>  .B \-\-lp\-tags
> diff --git a/src/lib/include/fwts_log.h b/src/lib/include/fwts_log.h
> index 8903bab..8027d41 100644
> --- a/src/lib/include/fwts_log.h
> +++ b/src/lib/include/fwts_log.h
> @@ -81,6 +81,7 @@ typedef struct fwts_log_ops_t {
>
>  fwts_log_ops fwts_log_plaintext_ops;
>  fwts_log_ops fwts_log_json_ops;
> +fwts_log_ops fwts_log_xml_ops;
>
>  extern fwts_log_field fwts_log_filter;
>  extern const char *fwts_log_format;
> diff --git a/src/lib/src/Makefile.am b/src/lib/src/Makefile.am
> index cae1f91..acea9cb 100644
> --- a/src/lib/src/Makefile.am
> +++ b/src/lib/src/Makefile.am
> @@ -39,6 +39,7 @@ libfwts_la_SOURCES = \
>        fwts_log.c \
>        fwts_log_plaintext.c \
>        fwts_log_json.c \
> +       fwts_log_xml.c \
>        fwts_memorymap.c \
>        fwts_microcode.c \
>        fwts_mmap.c \
> diff --git a/src/lib/src/fwts_framework.c b/src/lib/src/fwts_framework.c
> index 1fcd88e..2b42982 100644
> --- a/src/lib/src/fwts_framework.c
> +++ b/src/lib/src/fwts_framework.c
> @@ -76,7 +76,7 @@ static fwts_option fwts_framework_options[] = {
>        { "json-data-path",     "j:", 1, "Specify path to fwts json data files - default is /usr/share/fwts." },
>        { "lp-tags-log",        "",   0, "Output LaunchPad bug tags in results log." },
>        { "disassemble-aml",    "",   0, "Disassemble AML from DSDT and SSDT tables." },
> -       { "log-type",           "",   1, "Specify log type (plaintext or json)." },
> +       { "log-type",           "",   1, "Specify log type (plaintext, json or xml)." },
>        { NULL, NULL, 0, NULL }
>  };
>
> @@ -1040,6 +1040,8 @@ int fwts_framework_options_handler(fwts_framework *fw, int argc, char * const ar
>                                fw->log_type = LOG_TYPE_PLAINTEXT;
>                        else if (!strcmp(optarg, "json"))
>                                fw->log_type = LOG_TYPE_JSON;
> +                       else if (!strcmp(optarg, "xml"))
> +                               fw->log_type = LOG_TYPE_XML;
>                        else {
>                                fprintf(stderr, "--log-type can be either plaintext or json.\n");
>                                return FWTS_ERROR;
> diff --git a/src/lib/src/fwts_log.c b/src/lib/src/fwts_log.c
> index 5331fff..2b11441 100644
> --- a/src/lib/src/fwts_log.c
> +++ b/src/lib/src/fwts_log.c
> @@ -390,6 +390,9 @@ fwts_log *fwts_log_open(const char *owner, const char *name, const char *mode, f
>        case LOG_TYPE_PLAINTEXT:
>                newlog->ops = &fwts_log_plaintext_ops;
>                break;
> +       case LOG_TYPE_XML:
> +               newlog->ops = &fwts_log_xml_ops;
> +               break;
>        case LOG_TYPE_NONE:
>        default:
>                newlog->ops = &fwts_log_plaintext_ops;
> diff --git a/src/lib/src/fwts_log_xml.c b/src/lib/src/fwts_log_xml.c
> new file mode 100644
> index 0000000..57b530b
> --- /dev/null
> +++ b/src/lib/src/fwts_log_xml.c
> @@ -0,0 +1,186 @@
> +/*
> + * Copyright (C) 2010-2012 Canonical
> + *
> + * This program is free software; you can redistribute it and/or
> + * modify it under the terms of the GNU General Public License
> + * as published by the Free Software Foundation; either version 2
> + * of the License, or (at your option) any later version.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> + * GNU General Public License for more details.
> + *
> + * You should have received a copy of the GNU General Public License
> + * along with this program; if not, write to the Free Software
> + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
> + *
> + */
> +
> +#include <stdlib.h>
> +#include <stdio.h>
> +#include <stdarg.h>
> +#include <string.h>
> +#include <unistd.h>
> +#include <sys/ioctl.h>
> +#include <time.h>
> +
> +#include "fwts.h"
> +
> +#define MAX_XML_STACK  (64)
> +#define XML_INDENT     (4)
> +
> +typedef struct {
> +       const char *name;
> +} fwts_log_xml_stack_t;
> +
> +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
> + */
> +static int fwts_log_vprintf_xml(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)
> +{
> +       char buffer[4096];
> +       struct tm tm;
> +       time_t now;
> +       char *str;
> +
> +       if (!((field & LOG_FIELD_MASK) & fwts_log_filter))
> +               return 0;
> +
> +       if (field & (LOG_NEWLINE | LOG_SEPARATOR | LOG_DEBUG))
> +               return 0;
> +
> +       time(&now);
> +       localtime_r(&now, &tm);
> +
> +       fprintf(log->fp, "%*s<logentry>\n", xml_stack_index * XML_INDENT, "");
> +
> +       fprintf(log->fp, "%*s<line_num>%d</line_num>\n",
> +               (xml_stack_index + 1) * XML_INDENT,
> +               "", log->line_number);
> +
> +       fprintf(log->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",
> +               (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",
> +               (xml_stack_index + 1) * XML_INDENT,
> +               "", fwts_log_field_to_str_full(field));
> +
> +       str = fwts_log_level_to_str(level);
> +       if (!strcmp(str, " "))
> +               str = "None";
> +
> +       fprintf(log->fp, "%*s<level>%s</level>\n",
> +               (xml_stack_index + 1) * XML_INDENT, "", str);
> +
> +       fprintf(log->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",
> +               (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",
> +               (xml_stack_index + 1) * XML_INDENT,
> +               "", buffer);
> +
> +       fprintf(log->fp, "%*s</logentry>\n", xml_stack_index * XML_INDENT, "");
> +       fflush(log->fp);
> +
> +       log->line_number++;
> +
> +       return 0;
> +}
> +
> +/*
> + *  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)
> +{
> +       /* No-op for xml */
> +}
> +
> +/*
> + *  fwts_log_newline()
> + *     write newline to log
> + */
> +static void fwts_log_newline_xml(fwts_log *log)
> +{
> +       /* No-op for xml */
> +}
> +
> +static void fwts_log_section_begin_xml(fwts_log *log, 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);
> +
> +       if (xml_stack_index < MAX_XML_STACK)
> +               xml_stack_index++;
> +       else  {
> +               fprintf(stderr, "xml log stack overflow pushing section %s.\n", name);
> +               exit(EXIT_FAILURE);
> +       }
> +}
> +
> +static void fwts_log_section_end_xml(fwts_log *log)
> +{
> +       if (xml_stack_index > 0) {
> +               xml_stack_index--;
> +               fprintf(log->fp, "%*s</%s>\n", xml_stack_index * XML_INDENT,
> +                       "", xml_stack[xml_stack_index].name);
> +               fflush(log->fp);
> +       } else {
> +               fprintf(stderr, "xml log stack underflow.\n");
> +               exit(EXIT_FAILURE);
> +       }
> +
> +}
> +
> +static void fwts_log_open_xml(fwts_log *log)
> +{
> +       char *xml_header = "<?xml version=\"1.0\" encoding=\"UTF-8\" ?>\n";
> +
> +       fwrite(xml_header, 1, strlen(xml_header), log->fp);
> +       fflush(log->fp);
> +
> +       fwts_log_section_begin_xml(log, "fwts");
> +}
> +
> +static void fwts_log_close_xml(fwts_log *log)
> +{
> +       fwts_log_section_end_xml(log);
> +
> +       fwrite("\n", 1, 1, log->fp);
> +       fflush(log->fp);
> +}
> +
> +fwts_log_ops fwts_log_xml_ops = {
> +       .vprintf =       fwts_log_vprintf_xml,
> +       .underline =     fwts_log_underline_xml,
> +       .newline =       fwts_log_newline_xml,
> +       .section_begin = fwts_log_section_begin_xml,
> +       .section_end   = fwts_log_section_end_xml,
> +       .open          = fwts_log_open_xml,
> +       .close         = fwts_log_close_xml
> +};
> --
> 1.7.10
>
Acked-by: Keng-Yu Lin <kengyu@canonical.com>
Ivan Hu - June 1, 2012, 2:43 a.m.
On 05/28/2012 11:01 PM, Colin King wrote:
> From: Colin Ian King<colin.king@canonical.com>
>
> Signed-off-by: Colin Ian King<colin.king@canonical.com>
> ---
>   doc/fwts.1                   |    2 +-
>   src/lib/include/fwts_log.h   |    1 +
>   src/lib/src/Makefile.am      |    1 +
>   src/lib/src/fwts_framework.c |    4 +-
>   src/lib/src/fwts_log.c       |    3 +
>   src/lib/src/fwts_log_xml.c   |  186 ++++++++++++++++++++++++++++++++++++++++++
>   6 files changed, 195 insertions(+), 2 deletions(-)
>   create mode 100644 src/lib/src/fwts_log_xml.c
>
> diff --git a/doc/fwts.1 b/doc/fwts.1
> index 0d773cd..09eee56 100644
> --- a/doc/fwts.1
> +++ b/doc/fwts.1
> @@ -150,7 +150,7 @@ specify the information in each log line. The following specifiers are available
>   e.g. \-\-log\-format="%date %time [%field] (%owner): "
>   .TP
>   .B \-\-log\-type
> -specify the log type. Currently plaintext and json log types are available and the
> +specify the log type. Currently plaintext, json and xml log types are available and the
>   default is plaintext.
>   .TP
>   .B \-\-lp\-tags
> diff --git a/src/lib/include/fwts_log.h b/src/lib/include/fwts_log.h
> index 8903bab..8027d41 100644
> --- a/src/lib/include/fwts_log.h
> +++ b/src/lib/include/fwts_log.h
> @@ -81,6 +81,7 @@ typedef struct fwts_log_ops_t {
>
>   fwts_log_ops fwts_log_plaintext_ops;
>   fwts_log_ops fwts_log_json_ops;
> +fwts_log_ops fwts_log_xml_ops;
>
>   extern fwts_log_field fwts_log_filter;
>   extern const char *fwts_log_format;
> diff --git a/src/lib/src/Makefile.am b/src/lib/src/Makefile.am
> index cae1f91..acea9cb 100644
> --- a/src/lib/src/Makefile.am
> +++ b/src/lib/src/Makefile.am
> @@ -39,6 +39,7 @@ libfwts_la_SOURCES = \
>   	fwts_log.c \
>   	fwts_log_plaintext.c \
>   	fwts_log_json.c \
> +	fwts_log_xml.c \
>   	fwts_memorymap.c \
>   	fwts_microcode.c \
>   	fwts_mmap.c \
> diff --git a/src/lib/src/fwts_framework.c b/src/lib/src/fwts_framework.c
> index 1fcd88e..2b42982 100644
> --- a/src/lib/src/fwts_framework.c
> +++ b/src/lib/src/fwts_framework.c
> @@ -76,7 +76,7 @@ static fwts_option fwts_framework_options[] = {
>   	{ "json-data-path", 	"j:", 1, "Specify path to fwts json data files - default is /usr/share/fwts." },
>   	{ "lp-tags-log", 	"",   0, "Output LaunchPad bug tags in results log." },
>   	{ "disassemble-aml", 	"",   0, "Disassemble AML from DSDT and SSDT tables." },
> -	{ "log-type",		"",   1, "Specify log type (plaintext or json)." },
> +	{ "log-type",		"",   1, "Specify log type (plaintext, json or xml)." },
>   	{ NULL, NULL, 0, NULL }
>   };
>
> @@ -1040,6 +1040,8 @@ int fwts_framework_options_handler(fwts_framework *fw, int argc, char * const ar
>   				fw->log_type = LOG_TYPE_PLAINTEXT;
>   			else if (!strcmp(optarg, "json"))
>   				fw->log_type = LOG_TYPE_JSON;
> +			else if (!strcmp(optarg, "xml"))
> +				fw->log_type = LOG_TYPE_XML;
>   			else {
>   				fprintf(stderr, "--log-type can be either plaintext or json.\n");
>   				return FWTS_ERROR;
> diff --git a/src/lib/src/fwts_log.c b/src/lib/src/fwts_log.c
> index 5331fff..2b11441 100644
> --- a/src/lib/src/fwts_log.c
> +++ b/src/lib/src/fwts_log.c
> @@ -390,6 +390,9 @@ fwts_log *fwts_log_open(const char *owner, const char *name, const char *mode, f
>   	case LOG_TYPE_PLAINTEXT:
>   		newlog->ops =&fwts_log_plaintext_ops;
>   		break;
> +	case LOG_TYPE_XML:
> +		newlog->ops =&fwts_log_xml_ops;
> +		break;
>   	case LOG_TYPE_NONE:
>   	default:
>   		newlog->ops =&fwts_log_plaintext_ops;
> diff --git a/src/lib/src/fwts_log_xml.c b/src/lib/src/fwts_log_xml.c
> new file mode 100644
> index 0000000..57b530b
> --- /dev/null
> +++ b/src/lib/src/fwts_log_xml.c
> @@ -0,0 +1,186 @@
> +/*
> + * Copyright (C) 2010-2012 Canonical
> + *
> + * This program is free software; you can redistribute it and/or
> + * modify it under the terms of the GNU General Public License
> + * as published by the Free Software Foundation; either version 2
> + * of the License, or (at your option) any later version.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> + * GNU General Public License for more details.
> + *
> + * You should have received a copy of the GNU General Public License
> + * along with this program; if not, write to the Free Software
> + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
> + *
> + */
> +
> +#include<stdlib.h>
> +#include<stdio.h>
> +#include<stdarg.h>
> +#include<string.h>
> +#include<unistd.h>
> +#include<sys/ioctl.h>
> +#include<time.h>
> +
> +#include "fwts.h"
> +
> +#define MAX_XML_STACK	(64)
> +#define XML_INDENT	(4)
> +
> +typedef struct {
> +	const char *name;
> +} fwts_log_xml_stack_t;
> +
> +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
> + */
> +static int fwts_log_vprintf_xml(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)
> +{
> +	char buffer[4096];
> +	struct tm tm;
> +	time_t now;
> +	char *str;
> +
> +	if (!((field&  LOG_FIELD_MASK)&  fwts_log_filter))
> +		return 0;
> +
> +	if (field&  (LOG_NEWLINE | LOG_SEPARATOR | LOG_DEBUG))
> +		return 0;
> +
> +	time(&now);
> +	localtime_r(&now,&tm);
> +
> +	fprintf(log->fp, "%*s<logentry>\n", xml_stack_index * XML_INDENT, "");
> +
> +	fprintf(log->fp, "%*s<line_num>%d</line_num>\n",
> +		(xml_stack_index + 1) * XML_INDENT,
> +		"", log->line_number);
> +
> +	fprintf(log->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",
> +		(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",
> +		(xml_stack_index + 1) * XML_INDENT,
> +		"", fwts_log_field_to_str_full(field));
> +
> +	str = fwts_log_level_to_str(level);
> +	if (!strcmp(str, " "))
> +		str = "None";
> +
> +	fprintf(log->fp, "%*s<level>%s</level>\n",
> +		(xml_stack_index + 1) * XML_INDENT, "", str);
> +
> +	fprintf(log->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",
> +		(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",
> +		(xml_stack_index + 1) * XML_INDENT,
> +		"", buffer);
> +
> +	fprintf(log->fp, "%*s</logentry>\n", xml_stack_index * XML_INDENT, "");
> +	fflush(log->fp);
> +
> +	log->line_number++;
> +
> +	return 0;
> +}
> +
> +/*
> + *  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)
> +{
> +	/* No-op for xml */
> +}
> +
> +/*
> + *  fwts_log_newline()
> + *	write newline to log
> + */
> +static void fwts_log_newline_xml(fwts_log *log)
> +{
> +	/* No-op for xml */
> +}
> +
> +static void fwts_log_section_begin_xml(fwts_log *log, 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);
> +
> +	if (xml_stack_index<  MAX_XML_STACK)
> +		xml_stack_index++;
> +	else  {
> +		fprintf(stderr, "xml log stack overflow pushing section %s.\n", name);
> +		exit(EXIT_FAILURE);
> +	}
> +}
> +
> +static void fwts_log_section_end_xml(fwts_log *log)
> +{
> +	if (xml_stack_index>  0) {
> +		xml_stack_index--;
> +		fprintf(log->fp, "%*s</%s>\n", xml_stack_index * XML_INDENT,
> +			"", xml_stack[xml_stack_index].name);
> +		fflush(log->fp);
> +	} else {
> +		fprintf(stderr, "xml log stack underflow.\n");
> +		exit(EXIT_FAILURE);
> +	}
> +
> +}
> +
> +static void fwts_log_open_xml(fwts_log *log)
> +{
> +	char *xml_header = "<?xml version=\"1.0\" encoding=\"UTF-8\" ?>\n";
> +
> +	fwrite(xml_header, 1, strlen(xml_header), log->fp);
> +	fflush(log->fp);
> +
> +	fwts_log_section_begin_xml(log, "fwts");
> +}
> +
> +static void fwts_log_close_xml(fwts_log *log)
> +{
> +	fwts_log_section_end_xml(log);
> +
> +	fwrite("\n", 1, 1, log->fp);
> +	fflush(log->fp);
> +}
> +
> +fwts_log_ops fwts_log_xml_ops = {
> +	.vprintf = 	 fwts_log_vprintf_xml,
> +	.underline =	 fwts_log_underline_xml,
> +	.newline =	 fwts_log_newline_xml,
> +	.section_begin = fwts_log_section_begin_xml,
> +	.section_end   = fwts_log_section_end_xml,
> +	.open          = fwts_log_open_xml,
> +	.close	       = fwts_log_close_xml
> +};

Hi Colin,

I've checked the xml file(results.log). It seems that it doesn't close 
properly (missing </tests> and </fwts>).
Except that, all looks good to me. Thanks!

Best regards,
Ivan


Acked-by: Ivan Hu<ivan.hu@canonical.com>
Keng-Yu Lin - June 1, 2012, 8:21 a.m.
On Fri, Jun 1, 2012 at 10:43 AM, IvanHu <ivan.hu@canonical.com> wrote:
> On 05/28/2012 11:01 PM, Colin King wrote:
>>
>> From: Colin Ian King<colin.king@canonical.com>
>>
>> Signed-off-by: Colin Ian King<colin.king@canonical.com>
>> ---
>>  doc/fwts.1                   |    2 +-
>>  src/lib/include/fwts_log.h   |    1 +
>>  src/lib/src/Makefile.am      |    1 +
>>  src/lib/src/fwts_framework.c |    4 +-
>>  src/lib/src/fwts_log.c       |    3 +
>>  src/lib/src/fwts_log_xml.c   |  186
>> ++++++++++++++++++++++++++++++++++++++++++
>>  6 files changed, 195 insertions(+), 2 deletions(-)
>>  create mode 100644 src/lib/src/fwts_log_xml.c
>>
>> diff --git a/doc/fwts.1 b/doc/fwts.1
>> index 0d773cd..09eee56 100644
>> --- a/doc/fwts.1
>> +++ b/doc/fwts.1
>> @@ -150,7 +150,7 @@ specify the information in each log line. The
>> following specifiers are available
>>  e.g. \-\-log\-format="%date %time [%field] (%owner): "
>>  .TP
>>  .B \-\-log\-type
>> -specify the log type. Currently plaintext and json log types are
>> available and the
>> +specify the log type. Currently plaintext, json and xml log types are
>> available and the
>>  default is plaintext.
>>  .TP
>>  .B \-\-lp\-tags
>> diff --git a/src/lib/include/fwts_log.h b/src/lib/include/fwts_log.h
>> index 8903bab..8027d41 100644
>> --- a/src/lib/include/fwts_log.h
>> +++ b/src/lib/include/fwts_log.h
>> @@ -81,6 +81,7 @@ typedef struct fwts_log_ops_t {
>>
>>  fwts_log_ops fwts_log_plaintext_ops;
>>  fwts_log_ops fwts_log_json_ops;
>> +fwts_log_ops fwts_log_xml_ops;
>>
>>  extern fwts_log_field fwts_log_filter;
>>  extern const char *fwts_log_format;
>> diff --git a/src/lib/src/Makefile.am b/src/lib/src/Makefile.am
>> index cae1f91..acea9cb 100644
>> --- a/src/lib/src/Makefile.am
>> +++ b/src/lib/src/Makefile.am
>> @@ -39,6 +39,7 @@ libfwts_la_SOURCES = \
>>        fwts_log.c \
>>        fwts_log_plaintext.c \
>>        fwts_log_json.c \
>> +       fwts_log_xml.c \
>>        fwts_memorymap.c \
>>        fwts_microcode.c \
>>        fwts_mmap.c \
>> diff --git a/src/lib/src/fwts_framework.c b/src/lib/src/fwts_framework.c
>> index 1fcd88e..2b42982 100644
>> --- a/src/lib/src/fwts_framework.c
>> +++ b/src/lib/src/fwts_framework.c
>> @@ -76,7 +76,7 @@ static fwts_option fwts_framework_options[] = {
>>        { "json-data-path",     "j:", 1, "Specify path to fwts json data
>> files - default is /usr/share/fwts." },
>>        { "lp-tags-log",        "",   0, "Output LaunchPad bug tags in
>> results log." },
>>        { "disassemble-aml",    "",   0, "Disassemble AML from DSDT and
>> SSDT tables." },
>> -       { "log-type",           "",   1, "Specify log type (plaintext or
>> json)." },
>> +       { "log-type",           "",   1, "Specify log type (plaintext,
>> json or xml)." },
>>        { NULL, NULL, 0, NULL }
>>  };
>>
>> @@ -1040,6 +1040,8 @@ int fwts_framework_options_handler(fwts_framework
>> *fw, int argc, char * const ar
>>                                fw->log_type = LOG_TYPE_PLAINTEXT;
>>                        else if (!strcmp(optarg, "json"))
>>                                fw->log_type = LOG_TYPE_JSON;
>> +                       else if (!strcmp(optarg, "xml"))
>> +                               fw->log_type = LOG_TYPE_XML;
>>                        else {
>>                                fprintf(stderr, "--log-type can be either
>> plaintext or json.\n");
>>                                return FWTS_ERROR;
>> diff --git a/src/lib/src/fwts_log.c b/src/lib/src/fwts_log.c
>> index 5331fff..2b11441 100644
>> --- a/src/lib/src/fwts_log.c
>> +++ b/src/lib/src/fwts_log.c
>> @@ -390,6 +390,9 @@ fwts_log *fwts_log_open(const char *owner, const char
>> *name, const char *mode, f
>>        case LOG_TYPE_PLAINTEXT:
>>                newlog->ops =&fwts_log_plaintext_ops;
>>                break;
>> +       case LOG_TYPE_XML:
>> +               newlog->ops =&fwts_log_xml_ops;
>> +               break;
>>        case LOG_TYPE_NONE:
>>        default:
>>                newlog->ops =&fwts_log_plaintext_ops;
>> diff --git a/src/lib/src/fwts_log_xml.c b/src/lib/src/fwts_log_xml.c
>> new file mode 100644
>> index 0000000..57b530b
>> --- /dev/null
>> +++ b/src/lib/src/fwts_log_xml.c
>> @@ -0,0 +1,186 @@
>> +/*
>> + * Copyright (C) 2010-2012 Canonical
>> + *
>> + * This program is free software; you can redistribute it and/or
>> + * modify it under the terms of the GNU General Public License
>> + * as published by the Free Software Foundation; either version 2
>> + * of the License, or (at your option) any later version.
>> + *
>> + * This program is distributed in the hope that it will be useful,
>> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
>> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
>> + * GNU General Public License for more details.
>> + *
>> + * You should have received a copy of the GNU General Public License
>> + * along with this program; if not, write to the Free Software
>> + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
>> 02110-1301, USA.
>> + *
>> + */
>> +
>> +#include<stdlib.h>
>> +#include<stdio.h>
>> +#include<stdarg.h>
>> +#include<string.h>
>> +#include<unistd.h>
>> +#include<sys/ioctl.h>
>> +#include<time.h>
>> +
>> +#include "fwts.h"
>> +
>> +#define MAX_XML_STACK  (64)
>> +#define XML_INDENT     (4)
>> +
>> +typedef struct {
>> +       const char *name;
>> +} fwts_log_xml_stack_t;
>> +
>> +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
>> + */
>> +static int fwts_log_vprintf_xml(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)
>> +{
>> +       char buffer[4096];
>> +       struct tm tm;
>> +       time_t now;
>> +       char *str;
>> +
>> +       if (!((field&  LOG_FIELD_MASK)&  fwts_log_filter))
>> +               return 0;
>> +
>> +       if (field&  (LOG_NEWLINE | LOG_SEPARATOR | LOG_DEBUG))
>>
>> +               return 0;
>> +
>> +       time(&now);
>> +       localtime_r(&now,&tm);
>> +
>> +       fprintf(log->fp, "%*s<logentry>\n", xml_stack_index * XML_INDENT,
>> "");
>> +
>> +       fprintf(log->fp, "%*s<line_num>%d</line_num>\n",
>> +               (xml_stack_index + 1) * XML_INDENT,
>> +               "", log->line_number);
>> +
>> +       fprintf(log->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",
>> +               (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",
>> +               (xml_stack_index + 1) * XML_INDENT,
>> +               "", fwts_log_field_to_str_full(field));
>> +
>> +       str = fwts_log_level_to_str(level);
>> +       if (!strcmp(str, " "))
>> +               str = "None";
>> +
>> +       fprintf(log->fp, "%*s<level>%s</level>\n",
>> +               (xml_stack_index + 1) * XML_INDENT, "", str);
>> +
>> +       fprintf(log->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",
>> +               (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",
>> +               (xml_stack_index + 1) * XML_INDENT,
>> +               "", buffer);
>> +
>> +       fprintf(log->fp, "%*s</logentry>\n", xml_stack_index * XML_INDENT,
>> "");
>> +       fflush(log->fp);
>> +
>> +       log->line_number++;
>> +
>> +       return 0;
>> +}
>> +
>> +/*
>> + *  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)
>> +{
>> +       /* No-op for xml */
>> +}
>> +
>> +/*
>> + *  fwts_log_newline()
>> + *     write newline to log
>> + */
>> +static void fwts_log_newline_xml(fwts_log *log)
>> +{
>> +       /* No-op for xml */
>> +}
>> +
>> +static void fwts_log_section_begin_xml(fwts_log *log, 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);
>> +
>> +       if (xml_stack_index<  MAX_XML_STACK)
>> +               xml_stack_index++;
>> +       else  {
>> +               fprintf(stderr, "xml log stack overflow pushing section
>> %s.\n", name);
>> +               exit(EXIT_FAILURE);
>> +       }
>> +}
>> +
>> +static void fwts_log_section_end_xml(fwts_log *log)
>> +{
>> +       if (xml_stack_index>  0) {
>> +               xml_stack_index--;
>> +               fprintf(log->fp, "%*s</%s>\n", xml_stack_index *
>> XML_INDENT,
>> +                       "", xml_stack[xml_stack_index].name);
>> +               fflush(log->fp);
>> +       } else {
>> +               fprintf(stderr, "xml log stack underflow.\n");
>> +               exit(EXIT_FAILURE);
>> +       }
>> +
>> +}
>> +
>> +static void fwts_log_open_xml(fwts_log *log)
>> +{
>> +       char *xml_header = "<?xml version=\"1.0\" encoding=\"UTF-8\"
>> ?>\n";
>> +
>> +       fwrite(xml_header, 1, strlen(xml_header), log->fp);
>> +       fflush(log->fp);
>> +
>> +       fwts_log_section_begin_xml(log, "fwts");
>> +}
>> +
>> +static void fwts_log_close_xml(fwts_log *log)
>> +{
>> +       fwts_log_section_end_xml(log);
>> +
>> +       fwrite("\n", 1, 1, log->fp);
>> +       fflush(log->fp);
>> +}
>> +
>> +fwts_log_ops fwts_log_xml_ops = {
>> +       .vprintf =       fwts_log_vprintf_xml,
>> +       .underline =     fwts_log_underline_xml,
>> +       .newline =       fwts_log_newline_xml,
>> +       .section_begin = fwts_log_section_begin_xml,
>> +       .section_end   = fwts_log_section_end_xml,
>> +       .open          = fwts_log_open_xml,
>> +       .close         = fwts_log_close_xml
>> +};
>
>
> Hi Colin,
>
> I've checked the xml file(results.log). It seems that it doesn't close
> properly (missing </tests> and </fwts>).
> Except that, all looks good to me. Thanks!
>

I formed another patch fixing this.
Colin King - June 1, 2012, 8:54 a.m.
On 01/06/12 09:21, Keng-Yu Lin wrote:
> On Fri, Jun 1, 2012 at 10:43 AM, IvanHu <ivan.hu@canonical.com> wrote:
>> On 05/28/2012 11:01 PM, Colin King wrote:
>>>
>>> From: Colin Ian King<colin.king@canonical.com>
>>>
>>> Signed-off-by: Colin Ian King<colin.king@canonical.com>
>>> ---
>>>   doc/fwts.1                   |    2 +-
>>>   src/lib/include/fwts_log.h   |    1 +
>>>   src/lib/src/Makefile.am      |    1 +
>>>   src/lib/src/fwts_framework.c |    4 +-
>>>   src/lib/src/fwts_log.c       |    3 +
>>>   src/lib/src/fwts_log_xml.c   |  186
>>> ++++++++++++++++++++++++++++++++++++++++++
>>>   6 files changed, 195 insertions(+), 2 deletions(-)
>>>   create mode 100644 src/lib/src/fwts_log_xml.c
>>>
>>> diff --git a/doc/fwts.1 b/doc/fwts.1
>>> index 0d773cd..09eee56 100644
>>> --- a/doc/fwts.1
>>> +++ b/doc/fwts.1
>>> @@ -150,7 +150,7 @@ specify the information in each log line. The
>>> following specifiers are available
>>>   e.g. \-\-log\-format="%date %time [%field] (%owner): "
>>>   .TP
>>>   .B \-\-log\-type
>>> -specify the log type. Currently plaintext and json log types are
>>> available and the
>>> +specify the log type. Currently plaintext, json and xml log types are
>>> available and the
>>>   default is plaintext.
>>>   .TP
>>>   .B \-\-lp\-tags
>>> diff --git a/src/lib/include/fwts_log.h b/src/lib/include/fwts_log.h
>>> index 8903bab..8027d41 100644
>>> --- a/src/lib/include/fwts_log.h
>>> +++ b/src/lib/include/fwts_log.h
>>> @@ -81,6 +81,7 @@ typedef struct fwts_log_ops_t {
>>>
>>>   fwts_log_ops fwts_log_plaintext_ops;
>>>   fwts_log_ops fwts_log_json_ops;
>>> +fwts_log_ops fwts_log_xml_ops;
>>>
>>>   extern fwts_log_field fwts_log_filter;
>>>   extern const char *fwts_log_format;
>>> diff --git a/src/lib/src/Makefile.am b/src/lib/src/Makefile.am
>>> index cae1f91..acea9cb 100644
>>> --- a/src/lib/src/Makefile.am
>>> +++ b/src/lib/src/Makefile.am
>>> @@ -39,6 +39,7 @@ libfwts_la_SOURCES = \
>>>         fwts_log.c \
>>>         fwts_log_plaintext.c \
>>>         fwts_log_json.c \
>>> +       fwts_log_xml.c \
>>>         fwts_memorymap.c \
>>>         fwts_microcode.c \
>>>         fwts_mmap.c \
>>> diff --git a/src/lib/src/fwts_framework.c b/src/lib/src/fwts_framework.c
>>> index 1fcd88e..2b42982 100644
>>> --- a/src/lib/src/fwts_framework.c
>>> +++ b/src/lib/src/fwts_framework.c
>>> @@ -76,7 +76,7 @@ static fwts_option fwts_framework_options[] = {
>>>         { "json-data-path",     "j:", 1, "Specify path to fwts json data
>>> files - default is /usr/share/fwts." },
>>>         { "lp-tags-log",        "",   0, "Output LaunchPad bug tags in
>>> results log." },
>>>         { "disassemble-aml",    "",   0, "Disassemble AML from DSDT and
>>> SSDT tables." },
>>> -       { "log-type",           "",   1, "Specify log type (plaintext or
>>> json)." },
>>> +       { "log-type",           "",   1, "Specify log type (plaintext,
>>> json or xml)." },
>>>         { NULL, NULL, 0, NULL }
>>>   };
>>>
>>> @@ -1040,6 +1040,8 @@ int fwts_framework_options_handler(fwts_framework
>>> *fw, int argc, char * const ar
>>>                                 fw->log_type = LOG_TYPE_PLAINTEXT;
>>>                         else if (!strcmp(optarg, "json"))
>>>                                 fw->log_type = LOG_TYPE_JSON;
>>> +                       else if (!strcmp(optarg, "xml"))
>>> +                               fw->log_type = LOG_TYPE_XML;
>>>                         else {
>>>                                 fprintf(stderr, "--log-type can be either
>>> plaintext or json.\n");
>>>                                 return FWTS_ERROR;
>>> diff --git a/src/lib/src/fwts_log.c b/src/lib/src/fwts_log.c
>>> index 5331fff..2b11441 100644
>>> --- a/src/lib/src/fwts_log.c
>>> +++ b/src/lib/src/fwts_log.c
>>> @@ -390,6 +390,9 @@ fwts_log *fwts_log_open(const char *owner, const char
>>> *name, const char *mode, f
>>>         case LOG_TYPE_PLAINTEXT:
>>>                 newlog->ops =&fwts_log_plaintext_ops;
>>>                 break;
>>> +       case LOG_TYPE_XML:
>>> +               newlog->ops =&fwts_log_xml_ops;
>>> +               break;
>>>         case LOG_TYPE_NONE:
>>>         default:
>>>                 newlog->ops =&fwts_log_plaintext_ops;
>>> diff --git a/src/lib/src/fwts_log_xml.c b/src/lib/src/fwts_log_xml.c
>>> new file mode 100644
>>> index 0000000..57b530b
>>> --- /dev/null
>>> +++ b/src/lib/src/fwts_log_xml.c
>>> @@ -0,0 +1,186 @@
>>> +/*
>>> + * Copyright (C) 2010-2012 Canonical
>>> + *
>>> + * This program is free software; you can redistribute it and/or
>>> + * modify it under the terms of the GNU General Public License
>>> + * as published by the Free Software Foundation; either version 2
>>> + * of the License, or (at your option) any later version.
>>> + *
>>> + * This program is distributed in the hope that it will be useful,
>>> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
>>> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
>>> + * GNU General Public License for more details.
>>> + *
>>> + * You should have received a copy of the GNU General Public License
>>> + * along with this program; if not, write to the Free Software
>>> + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
>>> 02110-1301, USA.
>>> + *
>>> + */
>>> +
>>> +#include<stdlib.h>
>>> +#include<stdio.h>
>>> +#include<stdarg.h>
>>> +#include<string.h>
>>> +#include<unistd.h>
>>> +#include<sys/ioctl.h>
>>> +#include<time.h>
>>> +
>>> +#include "fwts.h"
>>> +
>>> +#define MAX_XML_STACK  (64)
>>> +#define XML_INDENT     (4)
>>> +
>>> +typedef struct {
>>> +       const char *name;
>>> +} fwts_log_xml_stack_t;
>>> +
>>> +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
>>> + */
>>> +static int fwts_log_vprintf_xml(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)
>>> +{
>>> +       char buffer[4096];
>>> +       struct tm tm;
>>> +       time_t now;
>>> +       char *str;
>>> +
>>> +       if (!((field&  LOG_FIELD_MASK)&  fwts_log_filter))
>>> +               return 0;
>>> +
>>> +       if (field&  (LOG_NEWLINE | LOG_SEPARATOR | LOG_DEBUG))
>>>
>>> +               return 0;
>>> +
>>> +       time(&now);
>>> +       localtime_r(&now,&tm);
>>> +
>>> +       fprintf(log->fp, "%*s<logentry>\n", xml_stack_index * XML_INDENT,
>>> "");
>>> +
>>> +       fprintf(log->fp, "%*s<line_num>%d</line_num>\n",
>>> +               (xml_stack_index + 1) * XML_INDENT,
>>> +               "", log->line_number);
>>> +
>>> +       fprintf(log->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",
>>> +               (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",
>>> +               (xml_stack_index + 1) * XML_INDENT,
>>> +               "", fwts_log_field_to_str_full(field));
>>> +
>>> +       str = fwts_log_level_to_str(level);
>>> +       if (!strcmp(str, " "))
>>> +               str = "None";
>>> +
>>> +       fprintf(log->fp, "%*s<level>%s</level>\n",
>>> +               (xml_stack_index + 1) * XML_INDENT, "", str);
>>> +
>>> +       fprintf(log->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",
>>> +               (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",
>>> +               (xml_stack_index + 1) * XML_INDENT,
>>> +               "", buffer);
>>> +
>>> +       fprintf(log->fp, "%*s</logentry>\n", xml_stack_index * XML_INDENT,
>>> "");
>>> +       fflush(log->fp);
>>> +
>>> +       log->line_number++;
>>> +
>>> +       return 0;
>>> +}
>>> +
>>> +/*
>>> + *  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)
>>> +{
>>> +       /* No-op for xml */
>>> +}
>>> +
>>> +/*
>>> + *  fwts_log_newline()
>>> + *     write newline to log
>>> + */
>>> +static void fwts_log_newline_xml(fwts_log *log)
>>> +{
>>> +       /* No-op for xml */
>>> +}
>>> +
>>> +static void fwts_log_section_begin_xml(fwts_log *log, 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);
>>> +
>>> +       if (xml_stack_index<  MAX_XML_STACK)
>>> +               xml_stack_index++;
>>> +       else  {
>>> +               fprintf(stderr, "xml log stack overflow pushing section
>>> %s.\n", name);
>>> +               exit(EXIT_FAILURE);
>>> +       }
>>> +}
>>> +
>>> +static void fwts_log_section_end_xml(fwts_log *log)
>>> +{
>>> +       if (xml_stack_index>  0) {
>>> +               xml_stack_index--;
>>> +               fprintf(log->fp, "%*s</%s>\n", xml_stack_index *
>>> XML_INDENT,
>>> +                       "", xml_stack[xml_stack_index].name);
>>> +               fflush(log->fp);
>>> +       } else {
>>> +               fprintf(stderr, "xml log stack underflow.\n");
>>> +               exit(EXIT_FAILURE);
>>> +       }
>>> +
>>> +}
>>> +
>>> +static void fwts_log_open_xml(fwts_log *log)
>>> +{
>>> +       char *xml_header = "<?xml version=\"1.0\" encoding=\"UTF-8\"
>>> ?>\n";
>>> +
>>> +       fwrite(xml_header, 1, strlen(xml_header), log->fp);
>>> +       fflush(log->fp);
>>> +
>>> +       fwts_log_section_begin_xml(log, "fwts");
>>> +}
>>> +
>>> +static void fwts_log_close_xml(fwts_log *log)
>>> +{
>>> +       fwts_log_section_end_xml(log);
>>> +
>>> +       fwrite("\n", 1, 1, log->fp);
>>> +       fflush(log->fp);
>>> +}
>>> +
>>> +fwts_log_ops fwts_log_xml_ops = {
>>> +       .vprintf =       fwts_log_vprintf_xml,
>>> +       .underline =     fwts_log_underline_xml,
>>> +       .newline =       fwts_log_newline_xml,
>>> +       .section_begin = fwts_log_section_begin_xml,
>>> +       .section_end   = fwts_log_section_end_xml,
>>> +       .open          = fwts_log_open_xml,
>>> +       .close         = fwts_log_close_xml
>>> +};
>>
>>
>> Hi Colin,
>>
>> I've checked the xml file(results.log). It seems that it doesn't close
>> properly (missing </tests> and </fwts>).
>> Except that, all looks good to me. Thanks!

Let me check why this is happening.

Colin
>>
>
> I formed another patch fixing this.
>
Colin King - June 1, 2012, 8:58 a.m.
On 01/06/12 09:21, Keng-Yu Lin wrote:
> On Fri, Jun 1, 2012 at 10:43 AM, IvanHu <ivan.hu@canonical.com> wrote:
>> On 05/28/2012 11:01 PM, Colin King wrote:
>>>
>>> From: Colin Ian King<colin.king@canonical.com>
>>>
>>> Signed-off-by: Colin Ian King<colin.king@canonical.com>
>>> ---
>>>   doc/fwts.1                   |    2 +-
>>>   src/lib/include/fwts_log.h   |    1 +
>>>   src/lib/src/Makefile.am      |    1 +
>>>   src/lib/src/fwts_framework.c |    4 +-
>>>   src/lib/src/fwts_log.c       |    3 +
>>>   src/lib/src/fwts_log_xml.c   |  186
>>> ++++++++++++++++++++++++++++++++++++++++++
>>>   6 files changed, 195 insertions(+), 2 deletions(-)
>>>   create mode 100644 src/lib/src/fwts_log_xml.c
>>>
>>> diff --git a/doc/fwts.1 b/doc/fwts.1
>>> index 0d773cd..09eee56 100644
>>> --- a/doc/fwts.1
>>> +++ b/doc/fwts.1
>>> @@ -150,7 +150,7 @@ specify the information in each log line. The
>>> following specifiers are available
>>>   e.g. \-\-log\-format="%date %time [%field] (%owner): "
>>>   .TP
>>>   .B \-\-log\-type
>>> -specify the log type. Currently plaintext and json log types are
>>> available and the
>>> +specify the log type. Currently plaintext, json and xml log types are
>>> available and the
>>>   default is plaintext.
>>>   .TP
>>>   .B \-\-lp\-tags
>>> diff --git a/src/lib/include/fwts_log.h b/src/lib/include/fwts_log.h
>>> index 8903bab..8027d41 100644
>>> --- a/src/lib/include/fwts_log.h
>>> +++ b/src/lib/include/fwts_log.h
>>> @@ -81,6 +81,7 @@ typedef struct fwts_log_ops_t {
>>>
>>>   fwts_log_ops fwts_log_plaintext_ops;
>>>   fwts_log_ops fwts_log_json_ops;
>>> +fwts_log_ops fwts_log_xml_ops;
>>>
>>>   extern fwts_log_field fwts_log_filter;
>>>   extern const char *fwts_log_format;
>>> diff --git a/src/lib/src/Makefile.am b/src/lib/src/Makefile.am
>>> index cae1f91..acea9cb 100644
>>> --- a/src/lib/src/Makefile.am
>>> +++ b/src/lib/src/Makefile.am
>>> @@ -39,6 +39,7 @@ libfwts_la_SOURCES = \
>>>         fwts_log.c \
>>>         fwts_log_plaintext.c \
>>>         fwts_log_json.c \
>>> +       fwts_log_xml.c \
>>>         fwts_memorymap.c \
>>>         fwts_microcode.c \
>>>         fwts_mmap.c \
>>> diff --git a/src/lib/src/fwts_framework.c b/src/lib/src/fwts_framework.c
>>> index 1fcd88e..2b42982 100644
>>> --- a/src/lib/src/fwts_framework.c
>>> +++ b/src/lib/src/fwts_framework.c
>>> @@ -76,7 +76,7 @@ static fwts_option fwts_framework_options[] = {
>>>         { "json-data-path",     "j:", 1, "Specify path to fwts json data
>>> files - default is /usr/share/fwts." },
>>>         { "lp-tags-log",        "",   0, "Output LaunchPad bug tags in
>>> results log." },
>>>         { "disassemble-aml",    "",   0, "Disassemble AML from DSDT and
>>> SSDT tables." },
>>> -       { "log-type",           "",   1, "Specify log type (plaintext or
>>> json)." },
>>> +       { "log-type",           "",   1, "Specify log type (plaintext,
>>> json or xml)." },
>>>         { NULL, NULL, 0, NULL }
>>>   };
>>>
>>> @@ -1040,6 +1040,8 @@ int fwts_framework_options_handler(fwts_framework
>>> *fw, int argc, char * const ar
>>>                                 fw->log_type = LOG_TYPE_PLAINTEXT;
>>>                         else if (!strcmp(optarg, "json"))
>>>                                 fw->log_type = LOG_TYPE_JSON;
>>> +                       else if (!strcmp(optarg, "xml"))
>>> +                               fw->log_type = LOG_TYPE_XML;
>>>                         else {
>>>                                 fprintf(stderr, "--log-type can be either
>>> plaintext or json.\n");
>>>                                 return FWTS_ERROR;
>>> diff --git a/src/lib/src/fwts_log.c b/src/lib/src/fwts_log.c
>>> index 5331fff..2b11441 100644
>>> --- a/src/lib/src/fwts_log.c
>>> +++ b/src/lib/src/fwts_log.c
>>> @@ -390,6 +390,9 @@ fwts_log *fwts_log_open(const char *owner, const char
>>> *name, const char *mode, f
>>>         case LOG_TYPE_PLAINTEXT:
>>>                 newlog->ops =&fwts_log_plaintext_ops;
>>>                 break;
>>> +       case LOG_TYPE_XML:
>>> +               newlog->ops =&fwts_log_xml_ops;
>>> +               break;
>>>         case LOG_TYPE_NONE:
>>>         default:
>>>                 newlog->ops =&fwts_log_plaintext_ops;
>>> diff --git a/src/lib/src/fwts_log_xml.c b/src/lib/src/fwts_log_xml.c
>>> new file mode 100644
>>> index 0000000..57b530b
>>> --- /dev/null
>>> +++ b/src/lib/src/fwts_log_xml.c
>>> @@ -0,0 +1,186 @@
>>> +/*
>>> + * Copyright (C) 2010-2012 Canonical
>>> + *
>>> + * This program is free software; you can redistribute it and/or
>>> + * modify it under the terms of the GNU General Public License
>>> + * as published by the Free Software Foundation; either version 2
>>> + * of the License, or (at your option) any later version.
>>> + *
>>> + * This program is distributed in the hope that it will be useful,
>>> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
>>> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
>>> + * GNU General Public License for more details.
>>> + *
>>> + * You should have received a copy of the GNU General Public License
>>> + * along with this program; if not, write to the Free Software
>>> + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
>>> 02110-1301, USA.
>>> + *
>>> + */
>>> +
>>> +#include<stdlib.h>
>>> +#include<stdio.h>
>>> +#include<stdarg.h>
>>> +#include<string.h>
>>> +#include<unistd.h>
>>> +#include<sys/ioctl.h>
>>> +#include<time.h>
>>> +
>>> +#include "fwts.h"
>>> +
>>> +#define MAX_XML_STACK  (64)
>>> +#define XML_INDENT     (4)
>>> +
>>> +typedef struct {
>>> +       const char *name;
>>> +} fwts_log_xml_stack_t;
>>> +
>>> +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
>>> + */
>>> +static int fwts_log_vprintf_xml(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)
>>> +{
>>> +       char buffer[4096];
>>> +       struct tm tm;
>>> +       time_t now;
>>> +       char *str;
>>> +
>>> +       if (!((field&  LOG_FIELD_MASK)&  fwts_log_filter))
>>> +               return 0;
>>> +
>>> +       if (field&  (LOG_NEWLINE | LOG_SEPARATOR | LOG_DEBUG))
>>>
>>> +               return 0;
>>> +
>>> +       time(&now);
>>> +       localtime_r(&now,&tm);
>>> +
>>> +       fprintf(log->fp, "%*s<logentry>\n", xml_stack_index * XML_INDENT,
>>> "");
>>> +
>>> +       fprintf(log->fp, "%*s<line_num>%d</line_num>\n",
>>> +               (xml_stack_index + 1) * XML_INDENT,
>>> +               "", log->line_number);
>>> +
>>> +       fprintf(log->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",
>>> +               (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",
>>> +               (xml_stack_index + 1) * XML_INDENT,
>>> +               "", fwts_log_field_to_str_full(field));
>>> +
>>> +       str = fwts_log_level_to_str(level);
>>> +       if (!strcmp(str, " "))
>>> +               str = "None";
>>> +
>>> +       fprintf(log->fp, "%*s<level>%s</level>\n",
>>> +               (xml_stack_index + 1) * XML_INDENT, "", str);
>>> +
>>> +       fprintf(log->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",
>>> +               (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",
>>> +               (xml_stack_index + 1) * XML_INDENT,
>>> +               "", buffer);
>>> +
>>> +       fprintf(log->fp, "%*s</logentry>\n", xml_stack_index * XML_INDENT,
>>> "");
>>> +       fflush(log->fp);
>>> +
>>> +       log->line_number++;
>>> +
>>> +       return 0;
>>> +}
>>> +
>>> +/*
>>> + *  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)
>>> +{
>>> +       /* No-op for xml */
>>> +}
>>> +
>>> +/*
>>> + *  fwts_log_newline()
>>> + *     write newline to log
>>> + */
>>> +static void fwts_log_newline_xml(fwts_log *log)
>>> +{
>>> +       /* No-op for xml */
>>> +}
>>> +
>>> +static void fwts_log_section_begin_xml(fwts_log *log, 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);
>>> +
>>> +       if (xml_stack_index<  MAX_XML_STACK)
>>> +               xml_stack_index++;
>>> +       else  {
>>> +               fprintf(stderr, "xml log stack overflow pushing section
>>> %s.\n", name);
>>> +               exit(EXIT_FAILURE);
>>> +       }
>>> +}
>>> +
>>> +static void fwts_log_section_end_xml(fwts_log *log)
>>> +{
>>> +       if (xml_stack_index>  0) {
>>> +               xml_stack_index--;
>>> +               fprintf(log->fp, "%*s</%s>\n", xml_stack_index *
>>> XML_INDENT,
>>> +                       "", xml_stack[xml_stack_index].name);
>>> +               fflush(log->fp);
>>> +       } else {
>>> +               fprintf(stderr, "xml log stack underflow.\n");
>>> +               exit(EXIT_FAILURE);
>>> +       }
>>> +
>>> +}
>>> +
>>> +static void fwts_log_open_xml(fwts_log *log)
>>> +{
>>> +       char *xml_header = "<?xml version=\"1.0\" encoding=\"UTF-8\"
>>> ?>\n";
>>> +
>>> +       fwrite(xml_header, 1, strlen(xml_header), log->fp);
>>> +       fflush(log->fp);
>>> +
>>> +       fwts_log_section_begin_xml(log, "fwts");
>>> +}
>>> +
>>> +static void fwts_log_close_xml(fwts_log *log)
>>> +{
>>> +       fwts_log_section_end_xml(log);
>>> +
>>> +       fwrite("\n", 1, 1, log->fp);
>>> +       fflush(log->fp);
>>> +}
>>> +
>>> +fwts_log_ops fwts_log_xml_ops = {
>>> +       .vprintf =       fwts_log_vprintf_xml,
>>> +       .underline =     fwts_log_underline_xml,
>>> +       .newline =       fwts_log_newline_xml,
>>> +       .section_begin = fwts_log_section_begin_xml,
>>> +       .section_end   = fwts_log_section_end_xml,
>>> +       .open          = fwts_log_open_xml,
>>> +       .close         = fwts_log_close_xml
>>> +};
>>
>>
>> Hi Colin,
>>
>> I've checked the xml file(results.log). It seems that it doesn't close
>> properly (missing </tests> and </fwts>).
>> Except that, all looks good to me. Thanks!

I'm finding this tricky to reproduce. Can you tell me how you tested 
this so I can track down the offending bugs?

Thanks!

Colin

>>
>
> I formed another patch fixing this.
>
Keng-Yu Lin - June 1, 2012, 9:14 a.m.
On Fri, Jun 1, 2012 at 4:58 PM, Colin Ian King <colin.king@canonical.com> wrote:
>
> I'm finding this tricky to reproduce. Can you tell me how you tested this so
> I can track down the offending bugs?
>
> Thanks!
>
> Colin

The attached result.log is an example of the xml log (with unmatched
tags in the end).

I pushed the my current working tree at
http://kernel.ubuntu.com/git?p=lexical/fwts.git;a=shortlog;h=refs/heads/xml-test.

Patch

diff --git a/doc/fwts.1 b/doc/fwts.1
index 0d773cd..09eee56 100644
--- a/doc/fwts.1
+++ b/doc/fwts.1
@@ -150,7 +150,7 @@  specify the information in each log line. The following specifiers are available
 e.g. \-\-log\-format="%date %time [%field] (%owner): "
 .TP
 .B \-\-log\-type
-specify the log type. Currently plaintext and json log types are available and the
+specify the log type. Currently plaintext, json and xml log types are available and the
 default is plaintext.
 .TP
 .B \-\-lp\-tags
diff --git a/src/lib/include/fwts_log.h b/src/lib/include/fwts_log.h
index 8903bab..8027d41 100644
--- a/src/lib/include/fwts_log.h
+++ b/src/lib/include/fwts_log.h
@@ -81,6 +81,7 @@  typedef struct fwts_log_ops_t {
 
 fwts_log_ops fwts_log_plaintext_ops;
 fwts_log_ops fwts_log_json_ops;
+fwts_log_ops fwts_log_xml_ops;
 
 extern fwts_log_field fwts_log_filter;
 extern const char *fwts_log_format;
diff --git a/src/lib/src/Makefile.am b/src/lib/src/Makefile.am
index cae1f91..acea9cb 100644
--- a/src/lib/src/Makefile.am
+++ b/src/lib/src/Makefile.am
@@ -39,6 +39,7 @@  libfwts_la_SOURCES = \
 	fwts_log.c \
 	fwts_log_plaintext.c \
 	fwts_log_json.c \
+	fwts_log_xml.c \
 	fwts_memorymap.c \
 	fwts_microcode.c \
 	fwts_mmap.c \
diff --git a/src/lib/src/fwts_framework.c b/src/lib/src/fwts_framework.c
index 1fcd88e..2b42982 100644
--- a/src/lib/src/fwts_framework.c
+++ b/src/lib/src/fwts_framework.c
@@ -76,7 +76,7 @@  static fwts_option fwts_framework_options[] = {
 	{ "json-data-path", 	"j:", 1, "Specify path to fwts json data files - default is /usr/share/fwts." },
 	{ "lp-tags-log", 	"",   0, "Output LaunchPad bug tags in results log." },
 	{ "disassemble-aml", 	"",   0, "Disassemble AML from DSDT and SSDT tables." },
-	{ "log-type",		"",   1, "Specify log type (plaintext or json)." },
+	{ "log-type",		"",   1, "Specify log type (plaintext, json or xml)." },
 	{ NULL, NULL, 0, NULL }
 };
 
@@ -1040,6 +1040,8 @@  int fwts_framework_options_handler(fwts_framework *fw, int argc, char * const ar
 				fw->log_type = LOG_TYPE_PLAINTEXT;
 			else if (!strcmp(optarg, "json"))
 				fw->log_type = LOG_TYPE_JSON;
+			else if (!strcmp(optarg, "xml"))
+				fw->log_type = LOG_TYPE_XML;
 			else {
 				fprintf(stderr, "--log-type can be either plaintext or json.\n");
 				return FWTS_ERROR;
diff --git a/src/lib/src/fwts_log.c b/src/lib/src/fwts_log.c
index 5331fff..2b11441 100644
--- a/src/lib/src/fwts_log.c
+++ b/src/lib/src/fwts_log.c
@@ -390,6 +390,9 @@  fwts_log *fwts_log_open(const char *owner, const char *name, const char *mode, f
 	case LOG_TYPE_PLAINTEXT:
 		newlog->ops = &fwts_log_plaintext_ops;
 		break;
+	case LOG_TYPE_XML:
+		newlog->ops = &fwts_log_xml_ops;
+		break;
 	case LOG_TYPE_NONE:
 	default:
 		newlog->ops = &fwts_log_plaintext_ops;
diff --git a/src/lib/src/fwts_log_xml.c b/src/lib/src/fwts_log_xml.c
new file mode 100644
index 0000000..57b530b
--- /dev/null
+++ b/src/lib/src/fwts_log_xml.c
@@ -0,0 +1,186 @@ 
+/*
+ * Copyright (C) 2010-2012 Canonical
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <stdarg.h>
+#include <string.h>
+#include <unistd.h>
+#include <sys/ioctl.h>
+#include <time.h>
+
+#include "fwts.h"
+
+#define MAX_XML_STACK	(64)
+#define XML_INDENT	(4)
+
+typedef struct {
+	const char *name;
+} fwts_log_xml_stack_t;
+
+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
+ */
+static int fwts_log_vprintf_xml(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)
+{
+	char buffer[4096];
+	struct tm tm;
+	time_t now;
+	char *str;
+
+	if (!((field & LOG_FIELD_MASK) & fwts_log_filter))
+		return 0;
+
+	if (field & (LOG_NEWLINE | LOG_SEPARATOR | LOG_DEBUG))
+		return 0;
+
+	time(&now);
+	localtime_r(&now, &tm);
+
+	fprintf(log->fp, "%*s<logentry>\n", xml_stack_index * XML_INDENT, "");
+
+	fprintf(log->fp, "%*s<line_num>%d</line_num>\n",
+		(xml_stack_index + 1) * XML_INDENT,
+		"", log->line_number);
+
+	fprintf(log->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",
+		(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",
+		(xml_stack_index + 1) * XML_INDENT,
+		"", fwts_log_field_to_str_full(field));
+
+	str = fwts_log_level_to_str(level);
+	if (!strcmp(str, " "))
+		str = "None";
+
+	fprintf(log->fp, "%*s<level>%s</level>\n",
+		(xml_stack_index + 1) * XML_INDENT, "", str);
+
+	fprintf(log->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",
+		(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",
+		(xml_stack_index + 1) * XML_INDENT,
+		"", buffer);
+
+	fprintf(log->fp, "%*s</logentry>\n", xml_stack_index * XML_INDENT, "");
+	fflush(log->fp);
+
+	log->line_number++;
+
+	return 0;
+}
+
+/*
+ *  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)
+{
+	/* No-op for xml */
+}
+
+/*
+ *  fwts_log_newline()
+ *	write newline to log
+ */
+static void fwts_log_newline_xml(fwts_log *log)
+{
+	/* No-op for xml */
+}
+
+static void fwts_log_section_begin_xml(fwts_log *log, 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);
+
+	if (xml_stack_index < MAX_XML_STACK)
+		xml_stack_index++;
+	else  {
+		fprintf(stderr, "xml log stack overflow pushing section %s.\n", name);
+		exit(EXIT_FAILURE);
+	}
+}
+
+static void fwts_log_section_end_xml(fwts_log *log)
+{
+	if (xml_stack_index > 0) {
+		xml_stack_index--;
+		fprintf(log->fp, "%*s</%s>\n", xml_stack_index * XML_INDENT,
+			"", xml_stack[xml_stack_index].name);
+		fflush(log->fp);
+	} else {
+		fprintf(stderr, "xml log stack underflow.\n");
+		exit(EXIT_FAILURE);
+	}
+
+}
+
+static void fwts_log_open_xml(fwts_log *log)
+{
+	char *xml_header = "<?xml version=\"1.0\" encoding=\"UTF-8\" ?>\n";
+
+	fwrite(xml_header, 1, strlen(xml_header), log->fp);
+	fflush(log->fp);
+
+	fwts_log_section_begin_xml(log, "fwts");
+}
+
+static void fwts_log_close_xml(fwts_log *log)
+{
+	fwts_log_section_end_xml(log);
+
+	fwrite("\n", 1, 1, log->fp);
+	fflush(log->fp);
+}
+
+fwts_log_ops fwts_log_xml_ops = {
+	.vprintf = 	 fwts_log_vprintf_xml,
+	.underline =	 fwts_log_underline_xml,
+	.newline =	 fwts_log_newline_xml,
+	.section_begin = fwts_log_section_begin_xml,
+	.section_end   = fwts_log_section_end_xml,
+	.open          = fwts_log_open_xml,
+	.close	       = fwts_log_close_xml
+};