Patchwork [5/8] lib: re-work logging to add in json formatted log output

login
register
mail settings
Submitter Colin King
Date May 16, 2012, 1:20 p.m.
Message ID <1337174425-21531-6-git-send-email-colin.king@canonical.com>
Download mbox | patch
Permalink /patch/159642/
State Accepted
Headers show

Comments

Colin King - May 16, 2012, 1:20 p.m.
From: Colin Ian King <colin.king@canonical.com>

Signed-off-by: Colin Ian King <colin.king@canonical.com>
---
 src/lib/include/fwts_framework.h |    3 +-
 src/lib/include/fwts_log.h       |   63 +++++++---
 src/lib/src/Makefile.am          |    2 +
 src/lib/src/fwts_framework.c     |   94 +++++++++++++--
 src/lib/src/fwts_log.c           |  239 ++++++++++++++++----------------------
 src/lib/src/fwts_log_json.c      |  185 +++++++++++++++++++++++++++++
 src/lib/src/fwts_log_plaintext.c |  195 +++++++++++++++++++++++++++++++
 src/lib/src/fwts_summary.c       |   16 ++-
 src/lib/src/fwts_tag.c           |    2 +-
 9 files changed, 623 insertions(+), 176 deletions(-)
 create mode 100644 src/lib/src/fwts_log_json.c
 create mode 100644 src/lib/src/fwts_log_plaintext.c
Alex Hung - May 21, 2012, 3:18 p.m.
On 05/16/2012 09:20 PM, Colin King wrote:
> From: Colin Ian King<colin.king@canonical.com>
>
> Signed-off-by: Colin Ian King<colin.king@canonical.com>

Acked-by: Alex Hung <alex.hung@canonical.com>
Keng-Yu Lin - May 23, 2012, 6:04 a.m.
On Wed, May 16, 2012 at 9:20 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>
> ---
>  src/lib/include/fwts_framework.h |    3 +-
>  src/lib/include/fwts_log.h       |   63 +++++++---
>  src/lib/src/Makefile.am          |    2 +
>  src/lib/src/fwts_framework.c     |   94 +++++++++++++--
>  src/lib/src/fwts_log.c           |  239 ++++++++++++++++----------------------
>  src/lib/src/fwts_log_json.c      |  185 +++++++++++++++++++++++++++++
>  src/lib/src/fwts_log_plaintext.c |  195 +++++++++++++++++++++++++++++++
>  src/lib/src/fwts_summary.c       |   16 ++-
>  src/lib/src/fwts_tag.c           |    2 +-
>  9 files changed, 623 insertions(+), 176 deletions(-)
>  create mode 100644 src/lib/src/fwts_log_json.c
>  create mode 100644 src/lib/src/fwts_log_plaintext.c
>
> 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);
>                }
>        }
> --
> 1.7.10
>
Acked-by: Keng-Yu Lin <kengyu@canonical.com>

Patch

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);
 		}
 	}