From 72f597065c60fbfca501a8d8c29e9a11cb740946 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Wed, 21 Dec 2011 18:17:22 +0100 Subject: [PATCH] journalctl: add json, export, short and verbose output modes --- src/journal/journalctl.c | 347 +++++++++++++++++++++++++++++++++++++++++++---- src/journal/sd-journal.c | 2 +- src/util.c | 31 +++-- src/util.h | 5 +- 4 files changed, 345 insertions(+), 40 deletions(-) diff --git a/src/journal/journalctl.c b/src/journal/journalctl.c index c947730..6f4342e 100644 --- a/src/journal/journalctl.c +++ b/src/journal/journalctl.c @@ -27,15 +27,328 @@ #include #include #include +#include #include "sd-journal.h" #include "log.h" +#include "util.h" -static bool arg_follow = true; +#define PRINT_THRESHOLD 128 + +static enum { + OUTPUT_SHORT, + OUTPUT_VERBOSE, + OUTPUT_EXPORT, + OUTPUT_JSON, + _OUTPUT_MAX +} arg_output = OUTPUT_JSON; + +static bool arg_follow = false; +static bool arg_show_all = false; + +static bool contains_unprintable(const void *p, size_t l) { + const char *j; + + for (j = p; j < (const char *) p + l; j++) + if (*j < ' ' || *j >= 127) + return true; + + return false; +} + +static int output_short(sd_journal *j, unsigned line) { + int r; + uint64_t realtime; + time_t t; + struct tm tm; + char buf[64]; + const void *data; + size_t length; + size_t n = 0; + + assert(j); + + r = sd_journal_get_realtime_usec(j, &realtime); + if (r < 0) { + log_error("Failed to get realtime: %s", strerror(-r)); + return r; + } + + t = (time_t) (realtime / USEC_PER_SEC); + if (strftime(buf, sizeof(buf), "%b %d %H:%M:%S", localtime_r(&t, &tm)) <= 0) { + log_error("Failed to format time."); + return -EINVAL; + } + + fputs(buf, stdout); + n += strlen(buf); + + if (sd_journal_get_data(j, "_HOSTNAME", &data, &length) >= 0 && + (arg_show_all || (!contains_unprintable(data, length) && + length < PRINT_THRESHOLD))) { + printf(" %.*s", (int) length - 10, ((const char*) data) + 10); + n += length - 10 + 1; + } + + if (sd_journal_get_data(j, "MESSAGE", &data, &length) >= 0) { + if (arg_show_all) + printf(" %.*s", (int) length - 8, ((const char*) data) + 8); + else if (contains_unprintable(data, length)) + fputs(" [blob data]", stdout); + else if (length - 8 + n < columns()) + printf(" %.*s", (int) length - 8, ((const char*) data) + 8); + else if (n < columns()) { + char *e; + + e = ellipsize_mem((const char *) data + 8, length - 8, columns() - n - 2, 90); + + if (!e) + printf(" %.*s", (int) length - 8, ((const char*) data) + 8); + else + printf(" %s", e); + + free(e); + } + } + + fputc('\n', stdout); + + return 0; +} + +static int output_verbose(sd_journal *j, unsigned line) { + const void *data; + size_t length; + char *cursor; + uint64_t realtime; + char ts[FORMAT_TIMESTAMP_MAX]; + int r; + + assert(j); + + r = sd_journal_get_realtime_usec(j, &realtime); + if (r < 0) { + log_error("Failed to get realtime timestamp: %s", strerror(-r)); + return r; + } + + r = sd_journal_get_cursor(j, &cursor); + if (r < 0) { + log_error("Failed to get cursor: %s", strerror(-r)); + return r; + } + + printf("%s [%s]\n", + format_timestamp(ts, sizeof(ts), realtime), + cursor); + + free(cursor); + + SD_JOURNAL_FOREACH_DATA(j, data, length) { + if (!arg_show_all && (length > PRINT_THRESHOLD || + contains_unprintable(data, length))) { + const char *c; + + c = memchr(data, '=', length); + if (!c) { + log_error("Invalid field."); + return -EINVAL; + } + + printf("\t%.*s=[blob data]\n", + (int) (c - (const char*) data), + (const char*) data); + } else + printf("\t%.*s\n", (int) length, (const char*) data); + } + + return 0; +} + +static int output_export(sd_journal *j, unsigned line) { + sd_id128_t boot_id; + char sid[33]; + int r; + usec_t realtime, monotonic; + char *cursor; + const void *data; + size_t length; + + assert(j); + + r = sd_journal_get_realtime_usec(j, &realtime); + if (r < 0) { + log_error("Failed to get realtime timestamp: %s", strerror(-r)); + return r; + } + + r = sd_journal_get_monotonic_usec(j, &monotonic, &boot_id); + if (r < 0) { + log_error("Failed to get monotonic timestamp: %s", strerror(-r)); + return r; + } + + r = sd_journal_get_cursor(j, &cursor); + if (r < 0) { + log_error("Failed to get cursor: %s", strerror(-r)); + return r; + } + + printf(".cursor=%s\n" + ".realtime=%llu\n" + ".monotonic=%llu\n" + ".boot_id=%s\n", + cursor, + (unsigned long long) realtime, + (unsigned long long) monotonic, + sd_id128_to_string(boot_id, sid)); + + free(cursor); + + SD_JOURNAL_FOREACH_DATA(j, data, length) { + + if (contains_unprintable(data, length)) { + const char *c; + uint64_t le64; + + c = memchr(data, '=', length); + if (!c) { + log_error("Invalid field."); + return -EINVAL; + } + + fwrite(data, c - (const char*) data, 1, stdout); + fputc('\n', stdout); + le64 = htole64(length - (c - (const char*) data) - 1); + fwrite(&le64, sizeof(le64), 1, stdout); + fwrite(c + 1, length - (c - (const char*) data) - 1, 1, stdout); + } else + fwrite(data, length, 1, stdout); + + fputc('\n', stdout); + } + + fputc('\n', stdout); + + return 0; +} + +static void json_escape(const char* p, size_t l) { + + if (contains_unprintable(p, l)) { + bool not_first = false; + + fputs("[ ", stdout); + + while (l > 0) { + if (not_first) + printf(", %u", (uint8_t) *p); + else { + not_first = true; + printf("%u", (uint8_t) *p); + } + + p++; + l--; + } + + fputs(" ]", stdout); + } else { + fputc('\"', stdout); + + while (l > 0) { + if (*p == '"' || *p == '\\') { + fputc('\\', stdout); + fputc(*p, stdout); + } else + fputc(*p, stdout); + + p++; + l--; + } + + fputc('\"', stdout); + } +} + +static int output_json(sd_journal *j, unsigned line) { + uint64_t realtime, monotonic; + char *cursor; + const void *data; + size_t length; + sd_id128_t boot_id; + char sid[33]; + int r; + + assert(j); + + r = sd_journal_get_realtime_usec(j, &realtime); + if (r < 0) { + log_error("Failed to get realtime timestamp: %s", strerror(-r)); + return r; + } + + r = sd_journal_get_monotonic_usec(j, &monotonic, &boot_id); + if (r < 0) { + log_error("Failed to get monotonic timestamp: %s", strerror(-r)); + return r; + } + + r = sd_journal_get_cursor(j, &cursor); + if (r < 0) { + log_error("Failed to get cursor: %s", strerror(-r)); + return r; + } + + if (line == 1) + fputc('\n', stdout); + else + fputs(",\n", stdout); + + printf("{\n" + "\t\".cursor\" : \"%s\",\n" + "\t\".realtime\" : %llu,\n" + "\t\".monotonic\" : %llu,\n" + "\t\".boot_id\" : \"%s\"", + cursor, + (unsigned long long) realtime, + (unsigned long long) monotonic, + sd_id128_to_string(boot_id, sid)); + + free(cursor); + + SD_JOURNAL_FOREACH_DATA(j, data, length) { + const char *c; + + c = memchr(data, '=', length); + if (!c) { + log_error("Invalid field."); + return -EINVAL; + } + + fputs(",\n\t", stdout); + json_escape(data, c - (const char*) data); + fputs(" : ", stdout); + json_escape(c + 1, length - (c - (const char*) data) - 1); + } + + fputs("\n}", stdout); + fflush(stdout); + + return 0; +} + +static int (*output_funcs[_OUTPUT_MAX])(sd_journal*j, unsigned line) = { + [OUTPUT_SHORT] = output_short, + [OUTPUT_VERBOSE] = output_verbose, + [OUTPUT_EXPORT] = output_export, + [OUTPUT_JSON] = output_json +}; int main(int argc, char *argv[]) { int r, i, fd; sd_journal *j = NULL; + unsigned line = 0; log_set_max_level(LOG_DEBUG); log_set_target(LOG_TARGET_CONSOLE); @@ -69,33 +382,18 @@ int main(int argc, char *argv[]) { goto finish; } + if (arg_output == OUTPUT_JSON) + fputc('[', stdout); + for (;;) { struct pollfd pollfd; while (sd_journal_next(j) > 0) { - const void *data; - size_t length; - char *cursor; - uint64_t realtime = 0, monotonic = 0; - - r = sd_journal_get_cursor(j, &cursor); - if (r < 0) { - log_error("Failed to get cursor: %s", strerror(-r)); - goto finish; - } - - printf("entry: %s\n", cursor); - free(cursor); + line ++; - sd_journal_get_realtime_usec(j, &realtime); - sd_journal_get_monotonic_usec(j, &monotonic, NULL); - printf("realtime: %llu\n" - "monotonic: %llu\n", - (unsigned long long) realtime, - (unsigned long long) monotonic); - - SD_JOURNAL_FOREACH_DATA(j, data, length) - printf("\t%.*s\n", (int) length, (const char*) data); + r = output_funcs[arg_output](j, line); + if (r < 0) + goto finish; } if (!arg_follow) @@ -121,6 +419,9 @@ int main(int argc, char *argv[]) { } } + if (arg_output == OUTPUT_JSON) + fputs("\n]\n", stdout); + finish: if (j) sd_journal_close(j); diff --git a/src/journal/sd-journal.c b/src/journal/sd-journal.c index bc575b4..b9abbdf 100644 --- a/src/journal/sd-journal.c +++ b/src/journal/sd-journal.c @@ -977,7 +977,7 @@ static int add_file(sd_journal *j, const char *prefix, const char *dir, const ch return r; } - journal_file_dump(f); + /* journal_file_dump(f); */ r = hashmap_put(j->files, f->path, f); if (r < 0) { diff --git a/src/util.c b/src/util.c index 37942de..1958354 100644 --- a/src/util.c +++ b/src/util.c @@ -3905,7 +3905,7 @@ char **replace_env_argv(char **argv, char **env) { return r; } -int columns(void) { +unsigned columns(void) { static __thread int parsed_columns = 0; const char *e; @@ -3948,38 +3948,41 @@ int running_in_chroot(void) { a.st_ino != b.st_ino; } -char *ellipsize(const char *s, unsigned length, unsigned percent) { - size_t l, x; +char *ellipsize_mem(const char *s, size_t old_length, size_t new_length, unsigned percent) { + size_t x; char *r; assert(s); assert(percent <= 100); - assert(length >= 3); + assert(new_length >= 3); - l = strlen(s); + if (old_length <= 3 || old_length <= new_length) + return strndup(s, old_length); - if (l <= 3 || l <= length) - return strdup(s); - - if (!(r = new0(char, length+1))) + r = new0(char, new_length+1); + if (!r) return r; - x = (length * percent) / 100; + x = (new_length * percent) / 100; - if (x > length - 3) - x = length - 3; + if (x > new_length - 3) + x = new_length - 3; memcpy(r, s, x); r[x] = '.'; r[x+1] = '.'; r[x+2] = '.'; memcpy(r + x + 3, - s + l - (length - x - 3), - length - x - 3); + s + old_length - (new_length - x - 3), + new_length - x - 3); return r; } +char *ellipsize(const char *s, size_t length, unsigned percent) { + return ellipsize_mem(s, strlen(s), length, percent); +} + int touch(const char *path) { int fd; diff --git a/src/util.h b/src/util.h index 1db82f8..ac2ec8c 100644 --- a/src/util.h +++ b/src/util.h @@ -378,11 +378,12 @@ void status_vprintf(const char *format, va_list ap); void status_printf(const char *format, ...); void status_welcome(void); -int columns(void); +unsigned columns(void); int running_in_chroot(void); -char *ellipsize(const char *s, unsigned length, unsigned percent); +char *ellipsize(const char *s, size_t length, unsigned percent); +char *ellipsize_mem(const char *s, size_t old_length, size_t new_length, unsigned percent); int touch(const char *path); -- 2.7.4