[PATCH 5/8] lib: re-work logging to add in json formatted log output
Keng-Yu Lin
kengyu at canonical.com
Wed May 23 06:04:16 UTC 2012
On Wed, May 16, 2012 at 9:20 PM, Colin King <colin.king at canonical.com> wrote:
> From: Colin Ian King <colin.king at canonical.com>
>
> Signed-off-by: Colin Ian King <colin.king at 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 at canonical.com>
More information about the fwts-devel
mailing list