diff --git a/src/lib/include/fwts_framework.h b/src/lib/include/fwts_framework.h
index 38879f6..fb74253 100644
--- a/src/lib/include/fwts_framework.h
+++ b/src/lib/include/fwts_framework.h
@@ -26,7 +26,6 @@
 
 #include "fwts_log.h"
 #include "fwts_list.h"
-#include "fwts_framework.h"
 
 #define FWTS_FRAMEWORK_MAGIC	0x2af61aec
 
@@ -139,6 +138,8 @@ typedef struct {
 
 	int firmware_type;			/* Type of firmware */
 	int show_progress;			/* Show progress while running current test */
+
+	fwts_log_type	log_type;		/* Output log type, default is plain text ASCII */
 } fwts_framework;
 
 typedef struct {
diff --git a/src/lib/include/fwts_log.h b/src/lib/include/fwts_log.h
index ab94029..8903bab 100644
--- a/src/lib/include/fwts_log.h
+++ b/src/lib/include/fwts_log.h
@@ -53,19 +53,43 @@ typedef enum {
 	LOG_LEVEL_INFO      = 0x00000010,
 } fwts_log_level;
 
+typedef enum {
+	LOG_TYPE_NONE       = 0x00000000,
+	LOG_TYPE_PLAINTEXT  = 0x00000001,
+	LOG_TYPE_JSON       = 0x00000002,
+	LOG_TYPE_XML        = 0x00000003,
+} fwts_log_type;
+
 typedef struct log_t {
 	unsigned int magic;
 	FILE *fp;	
 	char *owner;
 	int line_width;
 	int line_number;
+	struct fwts_log_ops_t *ops;
 } fwts_log;
 
-fwts_log *fwts_log_open(const char* owner, const char *name, const char *mode);
+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 *);
+} fwts_log_ops;
+
+fwts_log_ops fwts_log_plaintext_ops;
+fwts_log_ops fwts_log_json_ops;
+
+extern fwts_log_field fwts_log_filter;
+extern const char *fwts_log_format;
+
+fwts_log *fwts_log_open(const char* owner, const char *name, const char *mode, fwts_log_type);
 int       fwts_log_close(fwts_log *log);
-int       fwts_log_printf(fwts_log *log, const fwts_log_field field, const fwts_log_level level, const char *fmt, ...)
-	__attribute__((format(printf, 4, 5)));
-int       fwts_log_vprintf(fwts_log *log, const fwts_log_field field, const fwts_log_level level, const char *fmt, va_list args);
+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, ...)
+	__attribute__((format(printf, 7, 8)));
+int       fwts_log_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      fwts_log_newline(fwts_log *log);
 void      fwts_log_underline(fwts_log *log, const int ch);
 void      fwts_log_set_field_filter(const char *str);
@@ -75,47 +99,52 @@ void      fwts_log_print_fields(void);
 void      fwts_log_filter_set_field(const fwts_log_field filter);
 void      fwts_log_filter_unset_field(const fwts_log_field filter);
 int       fwts_log_str_to_level(const char *str);
+fwts_log_field fwts_log_str_to_field(const char *text);
 char     *fwts_log_level_to_str(const fwts_log_level level);
+char	 *fwts_log_field_to_str(const fwts_log_field field);
+char     *fwts_log_field_to_str_full(const fwts_log_field field);
 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);
 
 #define fwts_log_result(fw, fmt, args...)	\
-	fwts_log_printf(fw->results, LOG_RESULT, LOG_LEVEL_NONE, fmt, ## args)
+	fwts_log_printf(fw->results, LOG_RESULT, LOG_LEVEL_NONE, "", "", "", fmt, ## args)
 
 #define fwts_log_warning(fw, fmt, args...)	\
-	fwts_log_printf(fw->results, LOG_WARNING, LOG_LEVEL_NONE, fmt, ## args)
+	fwts_log_printf(fw->results, LOG_WARNING, LOG_LEVEL_NONE, "", "", "", fmt, ## args)
 
 #define fwts_log_warning_verbatum(fw, fmt, args...)	\
-	fwts_log_printf(fw->results, LOG_WARNING | LOG_VERBATUM, LOG_LEVEL_NONE, fmt, ## args)
+	fwts_log_printf(fw->results, LOG_WARNING | LOG_VERBATUM, LOG_LEVEL_NONE, "", "", "", fmt, ## args)
 	
 #define fwts_log_error(fw, fmt, args...)	\
-	fwts_log_printf(fw->results, LOG_ERROR, LOG_LEVEL_NONE, fmt, ## args)
+	fwts_log_printf(fw->results, LOG_ERROR, LOG_LEVEL_NONE, "", "", "", fmt, ## args)
 
 #define fwts_log_error_verbatum(fw, fmt, args...)	\
-	fwts_log_printf(fw->results, LOG_ERROR | LOG_VERBATUM, LOG_LEVEL_NONE, fmt, ## args)
+	fwts_log_printf(fw->results, LOG_ERROR | LOG_VERBATUM, LOG_LEVEL_NONE, "", "", "", fmt, ## args)
 
 #define fwts_log_info(fw, fmt, args...)	\
-	fwts_log_printf(fw->results, LOG_INFO, LOG_LEVEL_NONE, fmt, ## args)
+	fwts_log_printf(fw->results, LOG_INFO, LOG_LEVEL_NONE, "", "", "", fmt, ## args)
 
 #define fwts_log_info_verbatum(fw, fmt, args...)	\
-	fwts_log_printf(fw->results, LOG_INFO | LOG_VERBATUM, LOG_LEVEL_NONE, fmt, ## args)
+	fwts_log_printf(fw->results, LOG_INFO | LOG_VERBATUM, LOG_LEVEL_NONE, "", "", "", fmt, ## args)
 
 #define fwts_log_summary(fw, fmt, args...)	\
-	fwts_log_printf(fw->results, LOG_SUMMARY, LOG_LEVEL_NONE, fmt, ## args)
+	fwts_log_printf(fw->results, LOG_SUMMARY, LOG_LEVEL_NONE, "", "", "", fmt, ## args)
 
 #define fwts_log_summary_verbatum(fw, fmt, args...)	\
-	fwts_log_printf(fw->results, LOG_SUMMARY | LOG_VERBATUM, LOG_LEVEL_NONE, fmt, ## args)
+	fwts_log_printf(fw->results, LOG_SUMMARY | LOG_VERBATUM, LOG_LEVEL_NONE, "", "", "", fmt, ## args)
 
 #define fwts_log_advice(fw, fmt, args...)	\
-	fwts_log_printf(fw->results, LOG_ADVICE, LOG_LEVEL_NONE, fmt, ## args)
+	fwts_log_printf(fw->results, LOG_ADVICE, LOG_LEVEL_NONE, "", "", "", fmt, ## args)
 
 #define fwts_log_heading(fw, fmt, args...)	\
-	fwts_log_printf(fw->results, LOG_HEADING, LOG_LEVEL_NONE, fmt, ## args)
+	fwts_log_printf(fw->results, LOG_HEADING, LOG_LEVEL_NONE, "", "", "", fmt, ## args)
 
 #define fwts_log_tag(fw, fmt, args...)	\
-	fwts_log_printf(fw->results, LOG_TAG | LOG_VERBATUM, LOG_LEVEL_NONE, fmt, ## args)
+	fwts_log_printf(fw->results, LOG_TAG | LOG_VERBATUM, LOG_LEVEL_NONE, "", "", "", fmt, ## args)
 
 #define fwts_log_nl(fw) \
-	fwts_log_printf(fw->results, LOG_NEWLINE, LOG_LEVEL_NONE, "%s", "")
+	fwts_log_printf(fw->results, LOG_NEWLINE, LOG_LEVEL_NONE, "", "", "", "%s", "")
 
 #endif
diff --git a/src/lib/src/Makefile.am b/src/lib/src/Makefile.am
index 2aac13e..cae1f91 100644
--- a/src/lib/src/Makefile.am
+++ b/src/lib/src/Makefile.am
@@ -37,6 +37,8 @@ libfwts_la_SOURCES = \
 	fwts_klog.c \
 	fwts_list.c \
 	fwts_log.c \
+	fwts_log_plaintext.c \
+	fwts_log_json.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 da24b71..bc39b6b 100644
--- a/src/lib/src/fwts_framework.c
+++ b/src/lib/src/fwts_framework.c
@@ -76,6 +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)." },
 	{ NULL, NULL, 0, NULL }
 };
 
@@ -111,6 +112,39 @@ static fwts_framework_setting fwts_framework_settings[] = {
 	{ ID_NAME(FWTS_FRAMEWORK_INFOONLY),		"INFO", NULL },
 };
 
+#if 0
+static const char *fwts_framework_results_to_str(fwts_framework_results result)
+{
+	switch (result) {
+	case FWTS_FRAMEWORK_PASSED:
+		return "Passed";
+	case FWTS_FRAMEWORK_FAILED:	
+		return "Failed";
+	case FWTS_FRAMEWORK_FAILED_LOW:
+		return "Failed Low";
+	case FWTS_FRAMEWORK_FAILED_HIGH:
+		return "Failed High";
+	case FWTS_FRAMEWORK_FAILED_MEDIUM:
+		return "Failed Medium";
+	case FWTS_FRAMEWORK_FAILED_CRITICAL:
+		return "Failed Critical";
+	case FWTS_FRAMEWORK_WARNING:
+		return "Warning";
+	case FWTS_FRAMEWORK_ERROR:
+		return "Error";
+	case FWTS_FRAMEWORK_ADVICE:
+		return "Advice";
+	case FWTS_FRAMEWORK_SKIPPED:
+		return "Skipped";
+        case FWTS_FRAMEWORK_ABORTED:
+		return "Aborted";
+	case FWTS_FRAMEWORK_INFOONLY:
+		return "Info";
+	default:
+		return "Unknown";
+}
+#endif
+
 /*
  *  fwts_framework_compare_priority()
  *	used to register tests sorted on run priority
@@ -535,6 +569,7 @@ static int fwts_framework_run_test(fwts_framework *fw, const int num_tests, fwts
 
 	fw->failed_level = 0;
 
+	fwts_log_section_begin(fw->results, test->name);
 	fwts_log_set_owner(fw->results, test->name);
 
 	fw->current_minor_test_num = 1;
@@ -591,20 +626,27 @@ static int fwts_framework_run_test(fwts_framework *fw, const int num_tests, fwts
 		goto done;
 	}
 
+	fwts_log_section_begin(fw->results, "subtests");
 	for (minor_test = test->ops->minor_tests; 
 		*minor_test->test_func != NULL; 
 		minor_test++, fw->current_minor_test_num++) {
 
+		fwts_log_section_begin(fw->results, "subtest");
 		fw->current_minor_test_name = minor_test->name;
 
 		fwts_results_zero(&fw->minor_tests);
 
-		if (minor_test->name != NULL)
+		if (minor_test->name != NULL) {
+			fwts_log_section_begin(fw->results, "subtest_info");
 			fwts_log_info(fw, "Test %d of %d: %s",
 				fw->current_minor_test_num,
 				test->ops->total_tests, minor_test->name);
+			fwts_log_section_end(fw->results);
+		}
 
+		fwts_log_section_begin(fw->results, "subtest_results");
 		fwts_framework_minor_test_progress(fw, 0, "");
+
 		ret = (*minor_test->test_func)(fw);
 
 		/* Something went horribly wrong, abort all other tests too */
@@ -625,23 +667,33 @@ static int fwts_framework_run_test(fwts_framework *fw, const int num_tests, fwts
 			fprintf(stderr, "  %-55.55s %s\n", namebuf,
 				*resbuf ? resbuf : "     ");
 		}
+		fwts_log_section_end(fw->results);
 		fwts_log_nl(fw);
+		fwts_log_section_end(fw->results);
 	}
+	fwts_log_section_end(fw->results);
 
 	fwts_framework_summate_results(&fw->total, &fw->current_major_test->results);
 
 	if (test->ops->deinit)
 		test->ops->deinit(fw);
 
-	if (fw->flags & FWTS_FRAMEWORK_FLAGS_LP_TAGS_LOG)
+	if (fw->flags & FWTS_FRAMEWORK_FLAGS_LP_TAGS_LOG) {
+		fwts_log_section_begin(fw->results, "tags");
 		fwts_tag_report(fw, LOG_TAG, &fw->test_taglist);
+		fwts_log_section_end(fw->results);
+	}
 
 done:
 	fwts_list_free_items(&fw->test_taglist, free);
 
-	if (!(test->flags & FWTS_UTILS))
+	if (!(test->flags & FWTS_UTILS)) {
+		fwts_log_section_begin(fw->results, "results");
 		fwts_framework_test_summary(fw);
+		fwts_log_section_end(fw->results);
+	}
 
+	fwts_log_section_end(fw->results);
 	fwts_log_set_owner(fw->results, "fwts");
 
 	return FWTS_OK;
@@ -694,6 +746,7 @@ void fwts_framework_log(fwts_framework *fw,
 	const char *fmt, ...)
 {
 	char buffer[4096];
+	char prefix[256];
 	char *str = fwts_framework_get_env(result);
 
 	if (fmt) {
@@ -711,21 +764,24 @@ void fwts_framework_log(fwts_framework *fw,
 	switch (result) {
 	case FWTS_FRAMEWORK_ADVICE:
 		fwts_log_nl(fw);
-		fwts_log_printf(fw->results, LOG_RESULT, level, "%s: %s", str, buffer);
+		snprintf(prefix, sizeof(prefix), "%s: ", str);
+		fwts_log_printf(fw->results, LOG_RESULT, level, str, label, prefix, "%s", buffer);
 		fwts_log_nl(fw);
 		break;
 	case FWTS_FRAMEWORK_FAILED:
 		fw->failed_level |= level;
 		fwts_summary_add(fw, fw->current_major_test->name, level, buffer);
-		fwts_log_printf(fw->results, LOG_RESULT, level, "%s [%s] %s: Test %d, %s",
-			str, fwts_log_level_to_str(level), label, fw->current_minor_test_num, buffer);
+		snprintf(prefix, sizeof(prefix), "%s [%s] %s: Test %d, ",
+			str, fwts_log_level_to_str(level), label, fw->current_minor_test_num);
+		fwts_log_printf(fw->results, LOG_RESULT, level, str, label, prefix, "%s", buffer);
 		break;
 	case FWTS_FRAMEWORK_PASSED:
 	case FWTS_FRAMEWORK_WARNING:
 	case FWTS_FRAMEWORK_SKIPPED:
 	case FWTS_FRAMEWORK_ABORTED:
-		fwts_log_printf(fw->results, LOG_RESULT, level, "%s: Test %d, %s",
-			str, fw->current_minor_test_num, buffer);
+		snprintf(prefix, sizeof(prefix), "%s: Test %d, ",
+			str, fw->current_minor_test_num);
+		fwts_log_printf(fw->results, LOG_RESULT, level, str, label, prefix, "%s", buffer);
 		break;
 	case FWTS_FRAMEWORK_INFOONLY:
 		break;	/* no-op */
@@ -979,6 +1035,16 @@ int fwts_framework_options_handler(fwts_framework *fw, int argc, char * const ar
 		case 31: /* --disassemble-aml */
 			fwts_iasl_disassemble_all_to_file(fw);
 			return FWTS_COMPLETE;
+		case 32: /* --log-type */
+			if (!strcmp(optarg, "plaintext"))
+				fw->log_type = LOG_TYPE_PLAINTEXT;
+			else if (!strcmp(optarg, "json"))
+				fw->log_type = LOG_TYPE_JSON;
+			else {
+				fprintf(stderr, "--log-type can be either plaintext or json.\n");
+				return FWTS_ERROR;
+			}
+			break;
 		}
 		break;
 	case 'a': /* --all */
@@ -1124,8 +1190,9 @@ int fwts_framework_args(const int argc, char **argv)
 
 	/* Results log */
 	if ((fw->results = fwts_log_open("fwts",
-			fw->results_logname,
-			fw->flags & FWTS_FRAMEWORK_FLAGS_FORCE_CLEAN ? "w" : "a")) == NULL) {
+			fw->results_logname,	
+			fw->flags & FWTS_FRAMEWORK_FLAGS_FORCE_CLEAN ? "w" : "a",
+			fw->log_type)) == NULL) {
 		ret = FWTS_ERROR;
 		fprintf(stderr, "%s: Cannot open results log '%s'.\n", argv[0], fw->results_logname);
 		goto tidy_close;
@@ -1165,10 +1232,16 @@ int fwts_framework_args(const int argc, char **argv)
 			fwts_list_len(&tests_to_run),
 			fw->results_logname);
 
+	fwts_log_section_begin(fw->results, "heading");
 	fwts_framework_heading_info(fw, &tests_to_run);
+	fwts_log_section_end(fw->results);
+
+	fwts_log_section_begin(fw->results, "tests");
 	fwts_framework_tests_run(fw, &tests_to_run);
+	fwts_log_section_end(fw->results);
 
 	if (fw->print_summary) {
+		fwts_log_section_begin(fw->results, "summary");
 		fwts_log_set_owner(fw->results, "summary");
 		fwts_log_nl(fw);
 		if (fw->flags & FWTS_FRAMEWORK_FLAGS_LP_TAGS_LOG)
@@ -1176,6 +1249,7 @@ int fwts_framework_args(const int argc, char **argv)
 		fwts_framework_total_summary(fw);
 		fwts_log_nl(fw);
 		fwts_summary_report(fw, &fwts_framework_test_list);
+		fwts_log_section_end(fw->results);
 	}
 
 	if (fw->flags & FWTS_FRAMEWORK_FLAGS_LP_TAGS)
diff --git a/src/lib/src/fwts_log.c b/src/lib/src/fwts_log.c
index fabfcf5..5331fff 100644
--- a/src/lib/src/fwts_log.c
+++ b/src/lib/src/fwts_log.c
@@ -32,9 +32,9 @@
 
 static int log_line_width = 0;
 
-static fwts_log_field fwts_log_filter = ~0;
+fwts_log_field fwts_log_filter = ~0;
 
-static char fwts_log_format[256] = "";
+const char *fwts_log_format = "";
 
 /*
  *  fwts_log_set_line_width()
@@ -59,7 +59,7 @@ int fwts_log_line_number(fwts_log *log)
  *  fwts_log_field_to_str()
  *	return string name of log field
  */
-static char *fwts_log_field_to_str(const fwts_log_field field)
+char *fwts_log_field_to_str(const fwts_log_field field)
 {
 	switch (field & LOG_FIELD_MASK) {
 	case LOG_RESULT:
@@ -90,6 +90,40 @@ static char *fwts_log_field_to_str(const fwts_log_field field)
 }
 
 /*
+ *  fwts_log_field_to_str_full()
+ *	return full string name of log field
+ */
+char *fwts_log_field_to_str_full(const fwts_log_field field)
+{
+	switch (field & LOG_FIELD_MASK) {
+	case LOG_RESULT:
+		return "Result";
+	case LOG_ERROR:
+		return "Error";
+	case LOG_WARNING:
+		return "Warning";
+	case LOG_DEBUG:
+		return "Debug";
+	case LOG_INFO:
+		return "Info";
+	case LOG_SUMMARY:
+		return "Summary";
+	case LOG_SEPARATOR:
+		return "Separator";
+	case LOG_NEWLINE:
+		return "Newline";
+	case LOG_ADVICE:
+		return "Advice";
+	case LOG_HEADING:
+		return "Heading";
+	case LOG_TAG:
+		return "Tag";
+	default:
+		return "Unknown";
+	}
+}
+
+/*
  *  fwts_log_str_to_level()
  *	return log level mapped from the given string
  */
@@ -158,7 +192,7 @@ void fwts_log_print_fields(void)
  *  fwts_log_str_to_field()
  *	return log field of a given string, 0 if not matching
  */
-static fwts_log_field fwts_log_str_to_field(const char *text)
+fwts_log_field fwts_log_str_to_field(const char *text)
 {
 	int i;
 
@@ -238,79 +272,26 @@ void fwts_log_set_field_filter(const char *str)
  */
 void fwts_log_set_format(const char *str)
 {
-	strncpy(fwts_log_format, str, sizeof(fwts_log_format)-1);
-	fwts_log_format[sizeof(fwts_log_format)-1]='\0';
+	fwts_log_format = str;
 }
 
 /*
- *  fwts_log_header()
- *	format up a tabulated log heading
- */
-static int fwts_log_header(fwts_log *log, char *buffer, const int len, const fwts_log_field field, const fwts_log_level level)
-{
-	char *ptr;
-	int n = 0;
-	struct tm tm;
-	time_t now;
-
-	time(&now);
-	localtime_r(&now, &tm);
-
-	for (ptr = fwts_log_format; *ptr; ) {
-		if (*ptr == '%') {
-			ptr++;
-			if (!strncmp(ptr, "line", 4)) {
-				n += snprintf(buffer + n, len - n,
-					"%5.5d", log->line_number);
-				ptr += 4;
-			}
-			if (!strncmp(ptr, "date", 4)) {
-				n += snprintf(buffer + n, len - n,
-					"%2.2d/%2.2d/%-2.2d",
-					tm.tm_mday, tm.tm_mon + 1, (tm.tm_year+1900) % 100);
-				ptr += 4;
-			}
-			if (!strncmp(ptr, "time", 4)) {
-				n += snprintf(buffer + n, len - n,
-					"%2.2d:%2.2d:%2.2d",
-					tm.tm_hour, tm.tm_min, tm.tm_sec);
-				ptr += 4;
-			}
-			if (!strncmp(ptr, "field", 5)) {
-				n += snprintf(buffer + n, len - n, "%s",
-					fwts_log_field_to_str(field));
-				ptr += 5;
-			}
-			if (!strncmp(ptr, "level", 5)) {
-				n += snprintf(buffer + n, len - n, "%1.1s",
-					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);
-				ptr += 5;
-			}
-		}
-		else {
-			n += snprintf(buffer+n, len-n, "%c", *ptr);
-			ptr++;
-		}
-	}
-	return n;
-}
-
-
-/*
  *  fwts_log_vprintf()
  *	printf to a log
  */
-int fwts_log_printf(fwts_log *log, const fwts_log_field field, const fwts_log_level level, const char *fmt, ...)
+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;
 	int ret;
 
 	va_start(args, fmt);
-	ret = fwts_log_vprintf(log, field, level, fmt, args);
+	ret = fwts_log_vprintf(log, field, level, status, label, prefix, fmt, args);
 	va_end(args);
 
 	return ret;
@@ -320,53 +301,23 @@ int fwts_log_printf(fwts_log *log, const fwts_log_field field, const fwts_log_le
  *  fwts_log_vprintf()
  *	vprintf to a log
  */
-int fwts_log_vprintf(fwts_log *log, const fwts_log_field field, const fwts_log_level level, const char *fmt, va_list args)
+int fwts_log_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)
 {
-	char buffer[4096];
-	int n = 0;
-	int len = 0;
-
-	fwts_list *lines;
-	fwts_list_link *item;
-
-	if ((!log) || (log && log->magic != LOG_MAGIC))
-		return 0;
-
 	if (!((field & LOG_FIELD_MASK) & fwts_log_filter))
 		return 0;
 
-	/* This is a pain, we neen to find out how big the leading log
-	   message is, so format one up. */
-	n = fwts_log_header(log, buffer, sizeof(buffer), field, level);
-
-	vsnprintf(buffer+n, sizeof(buffer)-n, fmt, args);
-
-	/* Break text into multi-lines if necessary */
-	if (field & LOG_VERBATUM)
-		lines = fwts_list_from_text(buffer+n);
+	if (log && log->magic == LOG_MAGIC &&
+	    log->ops && log->ops->underline)
+		return log->ops->vprintf(log, field, level, status, label, prefix, fmt, args);
 	else
-		lines = fwts_format_text(buffer+n, log->line_width-n);
-
-	len = n;
-
-	fwts_list_foreach(item, lines) {
-		char *text = fwts_text_list_text(item);
-
-		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(log, buffer, sizeof(buffer), field, level);
-			fwrite(buffer, 1, n, log->fp);
-		}
-		fwrite(text, 1, strlen(text), log->fp);
-		fwrite("\n", 1, 1, log->fp);
-		fflush(log->fp);
-		log->line_number++;
-		len += strlen(text) + 1;
-	}
-	fwts_text_list_free(lines);
-
-	return len;
+		return 0;
 }
 
 /*
@@ -375,33 +326,9 @@ int fwts_log_vprintf(fwts_log *log, const fwts_log_field field, const fwts_log_l
  */
 void fwts_log_underline(fwts_log *log, const int ch)
 {
-	int n;
-	char *buffer;
-	size_t width;
-
-	if (!log || (log->magic != LOG_MAGIC))
-		return;
-
-	if (!((LOG_SEPARATOR & LOG_FIELD_MASK) & fwts_log_filter))
-		return;
-
-	width = log->line_width + 1;
-
-	buffer = calloc(1, width);
-	if (!buffer)
-		return;	/* Unlikely, and just abort */
-
-	/* Write in leading optional line prefix */
-	n = fwts_log_header(log, 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++;
-
-	free(buffer);
+	if (log && log->magic == LOG_MAGIC &&
+	    log->ops && log->ops->underline)
+		log->ops->underline(log, ch);
 }
 
 /*
@@ -410,11 +337,9 @@ void fwts_log_underline(fwts_log *log, const int ch)
  */
 void fwts_log_newline(fwts_log *log)
 {
-	if (log && (log->magic == LOG_MAGIC)) {
-		fwrite("\n", 1, 1, log->fp);
-		fflush(log->fp);
-		log->line_number++;
-	}
+	if (log && log->magic == LOG_MAGIC &&
+	    log->ops && log->ops->underline)
+		log->ops->newline(log);
 }
 
 int fwts_log_set_owner(fwts_log *log, const char *owner)
@@ -431,12 +356,26 @@ int fwts_log_set_owner(fwts_log *log, const char *owner)
 	return FWTS_ERROR;
 }
 
+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);
+}
+
+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);
+}
+
 /*
  *  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, const char *name, const char *mode)
+fwts_log *fwts_log_open(const char *owner, const char *name, const char *mode, fwts_log_type type)
 {
 	fwts_log *newlog;
 
@@ -444,6 +383,18 @@ fwts_log *fwts_log_open(const char *owner, const char *name, const char *mode)
 		return NULL;
 
 	newlog->magic = LOG_MAGIC;
+	switch (type) {
+	case LOG_TYPE_JSON:
+		newlog->ops = &fwts_log_json_ops;
+		break;
+	case LOG_TYPE_PLAINTEXT:
+		newlog->ops = &fwts_log_plaintext_ops;
+		break;
+	case LOG_TYPE_NONE:
+	default:
+		newlog->ops = &fwts_log_plaintext_ops;
+		break;
+	}
 
 	if (owner) {
 		if ((newlog->owner = calloc(1, strlen(owner)+1)) == NULL) {
@@ -469,6 +420,9 @@ fwts_log *fwts_log_open(const char *owner, const char *name, const char *mode)
 		newlog->line_width = fwts_tty_width(fileno(newlog->fp), LOG_LINE_WIDTH);
 	}
 
+	if (newlog->ops && newlog->ops->open)
+		newlog->ops->open(newlog);
+
 	return newlog;
 }
 
@@ -479,6 +433,9 @@ fwts_log *fwts_log_open(const char *owner, const char *name, const char *mode)
 int fwts_log_close(fwts_log *log)
 {
 	if (log && (log->magic == LOG_MAGIC)) {
+		if (log->ops && log->ops->close)
+			log->ops->close(log);
+
 		if (log->fp && (log->fp != stdout && log->fp != stderr))
 			fclose(log->fp);
 		if (log->owner)
diff --git a/src/lib/src/fwts_log_json.c b/src/lib/src/fwts_log_json.c
new file mode 100644
index 0000000..208847c
--- /dev/null
+++ b/src/lib/src/fwts_log_json.c
@@ -0,0 +1,185 @@
+/*
+ * 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.
+ *
+ */
+#define LOG_LINE_WIDTH 100
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <stdarg.h>
+#include <string.h>
+#include <unistd.h>
+#include <sys/ioctl.h>
+#include <time.h>
+
+#include <json/json.h>
+#include "fwts.h"
+
+#define MAX_JSON_STACK	(64)
+
+typedef struct {
+	json_object	*obj;
+	json_object	*log;
+} fwts_log_json_stack_t;
+
+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
+ */
+static int fwts_log_vprintf_json(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;
+	json_object *header;
+	json_object *json_log = (json_object*)json_stack[json_stack_index-1].log;
+	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);
+
+	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",
+		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",
+		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, "field_type",
+		json_object_new_string(fwts_log_field_to_str_full(field)));
+
+	str = fwts_log_level_to_str(level);
+	if (!strcmp(str, " "))
+		str = "None";
+	json_object_object_add(header, "level",
+		json_object_new_string(str));
+
+	json_object_object_add(header, "status", json_object_new_string(*status ? status : "None"));
+	json_object_object_add(header, "failure_label", json_object_new_string(label && *label ? label : "None"));
+
+	/* Redundant really
+	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;
+}
+
+/*
+ *  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)
+{
+	/* No-op for json */
+}
+
+/*
+ *  fwts_log_newline()
+ *	write newline to log
+ */
+static void fwts_log_newline_json(fwts_log *log)
+{
+	/* No-op for json */
+}
+
+static void fwts_log_section_begin_json(fwts_log *log, const char *name)
+{
+	json_object *json_obj;
+	json_object *json_log;
+
+	json_obj = json_object_new_object();
+	json_log = json_object_new_array();
+	json_object_object_add(json_obj, name, json_log);
+
+	json_stack[json_stack_index].obj = json_obj;
+	json_stack[json_stack_index].log = json_log;
+
+	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)
+		json_stack_index++;
+	else  {
+		fprintf(stderr, "json log stack overflow pushing section %s.\n", name);
+		exit(EXIT_FAILURE);
+	}
+}
+
+static void fwts_log_section_end_json(fwts_log *log)
+{
+	if (json_stack_index > 0)
+		json_stack_index--;
+	else {
+		fprintf(stderr, "json log stack underflow.\n");
+		exit(EXIT_FAILURE);
+	}
+}
+
+static void fwts_log_open_json(fwts_log *log)
+{
+	fwts_log_section_begin_json(log, "fwts");
+}
+
+static void fwts_log_close_json(fwts_log *log)
+{
+	const char *str;
+	size_t len;
+
+	fwts_log_section_end_json(log);
+
+	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);
+	json_object_put(json_stack[0].obj);
+}
+
+fwts_log_ops fwts_log_json_ops = {
+	.vprintf = 	 fwts_log_vprintf_json,
+	.underline =	 fwts_log_underline_json,
+	.newline =	 fwts_log_newline_json,
+	.section_begin = fwts_log_section_begin_json,
+	.section_end   = fwts_log_section_end_json,
+	.open          = fwts_log_open_json,
+	.close	       = fwts_log_close_json
+};
diff --git a/src/lib/src/fwts_log_plaintext.c b/src/lib/src/fwts_log_plaintext.c
new file mode 100644
index 0000000..44c443f
--- /dev/null
+++ b/src/lib/src/fwts_log_plaintext.c
@@ -0,0 +1,195 @@
+/*
+ * 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.
+ *
+ */
+#define LOG_LINE_WIDTH 100
+
+#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"
+
+/*
+ *  fwts_log_header_plaintext()
+ *	format up a tabulated log heading
+ */
+static int fwts_log_header_plaintext(fwts_log *log,
+	char *buffer,
+	const int len,
+	const fwts_log_field field,
+	const fwts_log_level level)
+{
+	const char *ptr;
+	int n = 0;
+	struct tm tm;
+	time_t now;
+
+	time(&now);
+	localtime_r(&now, &tm);
+
+	for (ptr = fwts_log_format; *ptr; ) {
+		if (*ptr == '%') {
+			ptr++;
+			if (!strncmp(ptr, "line", 4)) {
+				n += snprintf(buffer + n, len - n,
+					"%5.5d", log->line_number);
+				ptr += 4;
+			}
+			if (!strncmp(ptr, "date", 4)) {
+				n += snprintf(buffer + n, len - n,
+					"%2.2d/%2.2d/%-2.2d",
+					tm.tm_mday, tm.tm_mon + 1, (tm.tm_year+1900) % 100);
+				ptr += 4;
+			}
+			if (!strncmp(ptr, "time", 4)) {
+				n += snprintf(buffer + n, len - n,
+					"%2.2d:%2.2d:%2.2d",
+					tm.tm_hour, tm.tm_min, tm.tm_sec);
+				ptr += 4;
+			}
+			if (!strncmp(ptr, "field", 5)) {
+				n += snprintf(buffer + n, len - n, "%s",
+					fwts_log_field_to_str(field));
+				ptr += 5;
+			}
+			if (!strncmp(ptr, "level", 5)) {
+				n += snprintf(buffer + n, len - n, "%1.1s",
+					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);
+				ptr += 5;
+			}
+		} else {
+			n += snprintf(buffer+n, len-n, "%c", *ptr);
+			ptr++;
+		}
+	}
+	return n;
+}
+
+
+/*
+ *  fwts_log_vprintf()
+ *	vprintf to a log
+ */
+static int fwts_log_vprintf_plaintext(fwts_log *log,
+	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)
+{
+	char buffer[4096];
+	int n = 0;
+	int header_len;
+	int len = 0;
+
+	fwts_list *lines;
+	fwts_list_link *item;
+
+	if (!((field & LOG_FIELD_MASK) & fwts_log_filter))
+		return 0;
+
+	/* 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);
+
+	/* Break text into multi-lines if necessary */
+	if (field & LOG_VERBATUM)
+		lines = fwts_list_from_text(buffer + header_len);
+	else
+		lines = fwts_format_text(buffer + header_len, log->line_width - header_len);
+
+	len = n;
+
+	fwts_list_foreach(item, lines) {
+		char *text = fwts_text_list_text(item);
+
+		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);
+		}
+		fwrite(text, 1, strlen(text), log->fp);
+		fwrite("\n", 1, 1, log->fp);
+		fflush(log->fp);
+		log->line_number++;
+		len += strlen(text) + 1;
+	}
+	fwts_text_list_free(lines);
+	
+	return len;
+}
+
+/*
+ *  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)
+{
+	int n;
+	char *buffer;
+	size_t width = log->line_width + 1;
+
+	if (!((LOG_SEPARATOR & LOG_FIELD_MASK) & fwts_log_filter))
+		return;
+
+	buffer = calloc(1, width);
+	if (!buffer)
+		return;	/* Unlikely, and just abort */
+
+	/* Write in leading optional line prefix */
+	n = fwts_log_header_plaintext(log, 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++;
+
+	free(buffer);
+}
+
+/*
+ *  fwts_log_newline_plaintext()
+ *	write newline to log
+ */
+static void fwts_log_newline_plaintext(fwts_log *log)
+{
+	fwrite("\n", 1, 1, log->fp);
+	fflush(log->fp);
+	log->line_number++;
+}
+
+fwts_log_ops fwts_log_plaintext_ops = {
+	.vprintf = 	fwts_log_vprintf_plaintext,
+	.underline =	fwts_log_underline_plaintext,
+	.newline =	fwts_log_newline_plaintext
+};
diff --git a/src/lib/src/fwts_summary.c b/src/lib/src/fwts_summary.c
index 192043d..38d6a79 100644
--- a/src/lib/src/fwts_summary.c
+++ b/src/lib/src/fwts_summary.c
@@ -210,34 +210,38 @@ int fwts_summary_report(fwts_framework *fw, fwts_list *test_list)
 	fwts_list_link *item;
 
 	fwts_log_summary(fw, "Test Failure Summary");
-	fwts_log_summary(fw, "====================");
+	fwts_log_underline(fw->results, '=');
 	fwts_log_nl(fw);
 
 	for (i=0;i<SUMMARY_MAX;i++) {
+		fwts_log_section_begin(fw->results, "failure");
+
 		if (fwts_summaries[i]->len) {
 			fwts_list_link *item;
 			fwts_log_summary(fw, "%s failures: %d", summary_names[i], fwts_summaries[i]->len);
 
+			fwts_log_section_begin(fw->results, "failures");
 			fwts_list_foreach(item, fwts_summaries[i]) {
 				fwts_summary_item *summary_item = fwts_list_data(fwts_summary_item *,item);
 				char *lines = fwts_summary_lines(&summary_item->log_lines);
-				fwts_log_summary(fw, " %s test, at %d log line%s: %s",
+				fwts_log_summary(fw, " %s test, at %d log line%s: %s: %s",
 					summary_item->test,
 					fwts_list_len(&summary_item->log_lines),
 					fwts_list_len(&summary_item->log_lines) > 1 ? "s" : "",
-					lines);
-				free(lines);
-				fwts_log_summary_verbatum(fw, "  \"%s\"",
+					lines,
 					summary_item->text);
+				free(lines);
 			}
+			fwts_log_section_end(fw->results);
 		}
 		else
 			fwts_log_summary(fw, "%s failures: NONE", summary_names[i]);
 
+		fwts_log_section_end(fw->results);
 		fwts_log_nl(fw);
 	}
 
-	if (fw->total_run > 0) {		
+	if (fw->log_type == LOG_TYPE_PLAINTEXT && fw->total_run > 0) {		
 		sorted = fwts_list_new();
 		fwts_list_foreach(item, test_list)
 			fwts_list_add_ordered(sorted, fwts_list_data(fwts_framework_test *,item), fwts_framework_compare_test_name);
diff --git a/src/lib/src/fwts_tag.c b/src/lib/src/fwts_tag.c
index c1ba33d..38d1449 100644
--- a/src/lib/src/fwts_tag.c
+++ b/src/lib/src/fwts_tag.c
@@ -180,7 +180,7 @@ void fwts_tag_report(fwts_framework *fw, const fwts_log_field field, fwts_list *
 	if ((taglist != NULL) && (fwts_list_len(taglist) > 0)) {
 		char *tags = fwts_tag_list_to_str(taglist);
 		if (tags) {
-			fwts_log_printf(fw->results, field | LOG_VERBATUM, LOG_LEVEL_NONE, "Tags: %s", tags);
+			fwts_log_printf(fw->results, field | LOG_VERBATUM, LOG_LEVEL_NONE, "", "", "", "Tags: %s", tags);
 			free(tags);
 		}
 	}
