From patchwork Wed May 16 13:20:22 2012 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Colin Ian King X-Patchwork-Id: 159642 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Received: from chlorine.canonical.com (chlorine.canonical.com [91.189.94.204]) by ozlabs.org (Postfix) with ESMTP id B3EC8B6FAC for ; Wed, 16 May 2012 23:20:43 +1000 (EST) Received: from localhost ([127.0.0.1] helo=chlorine.canonical.com) by chlorine.canonical.com with esmtp (Exim 4.71) (envelope-from ) id 1SUe9i-0003Ex-FG for incoming@patchwork.ozlabs.org; Wed, 16 May 2012 13:20:42 +0000 Received: from youngberry.canonical.com ([91.189.89.112]) by chlorine.canonical.com with esmtp (Exim 4.71) (envelope-from ) id 1SUe9e-0003EK-A5 for fwts-devel@lists.ubuntu.com; Wed, 16 May 2012 13:20:38 +0000 Received: from cpc19-craw6-2-0-cust5.croy.cable.virginmedia.com ([77.102.228.6] helo=localhost) by youngberry.canonical.com with esmtpsa (TLS1.0:DHE_RSA_AES_128_CBC_SHA1:16) (Exim 4.71) (envelope-from ) id 1SUe9c-0007VV-LK for fwts-devel@lists.ubuntu.com; Wed, 16 May 2012 13:20:38 +0000 From: Colin King To: fwts-devel@lists.ubuntu.com Subject: [PATCH 5/8] lib: re-work logging to add in json formatted log output Date: Wed, 16 May 2012 14:20:22 +0100 Message-Id: <1337174425-21531-6-git-send-email-colin.king@canonical.com> X-Mailer: git-send-email 1.7.10 In-Reply-To: <1337174425-21531-1-git-send-email-colin.king@canonical.com> References: <1337174425-21531-1-git-send-email-colin.king@canonical.com> X-BeenThere: fwts-devel@lists.ubuntu.com X-Mailman-Version: 2.1.13 Precedence: list List-Id: Firmware Test Suite Development List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , MIME-Version: 1.0 Sender: fwts-devel-bounces@lists.ubuntu.com Errors-To: fwts-devel-bounces@lists.ubuntu.com From: Colin Ian King Signed-off-by: Colin Ian King Acked-by: Alex Hung Acked-by: Keng-Yu Lin --- 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 +#include +#include +#include +#include +#include +#include + +#include +#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 +#include +#include +#include +#include +#include +#include + +#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;iresults, "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); } }