[PATCH 5/8] lib: re-work logging to add in json formatted log output
Colin King
colin.king at canonical.com
Wed May 16 13:20:22 UTC 2012
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
More information about the fwts-devel
mailing list