#include "log.h"
#include "util.h"
#include "utf8.h"
+#include "hashmap.h"
#define PRINT_THRESHOLD 128
#define JSON_THRESHOLD 4096
+static int print_catalog(FILE *f, sd_journal *j) {
+ int r;
+ _cleanup_free_ char *t = NULL, *z = NULL;
+
+
+ r = sd_journal_get_catalog(j, &t);
+ if (r < 0)
+ return r;
+
+ z = strreplace(strstrip(t), "\n", "\n-- ");
+ if (!z)
+ return log_oom();
+
+ fputs("-- ", f);
+ fputs(z, f);
+ fputc('\n', f);
+
+ return 0;
+}
+
static int parse_field(const void *data, size_t length, const char *field, char **target, size_t *target_size) {
size_t fl, nl;
void *buf;
if (flags & OUTPUT_SHOW_ALL)
return true;
- if (l > PRINT_THRESHOLD)
+ if (l >= PRINT_THRESHOLD)
return false;
if (!utf8_is_printable_n(p, l))
assert(f);
assert(j);
+ sd_journal_set_data_threshold(j, flags & OUTPUT_SHOW_ALL ? 0 : PRINT_THRESHOLD);
+
SD_JOURNAL_FOREACH_DATA(j, data, length) {
r = parse_field(data, length, "PRIORITY=", &priority, &priority_len);
if (!message)
return 0;
+ if (!(flags & OUTPUT_SHOW_ALL))
+ strip_tab_ansi(&message, &message_len);
+
if (priority_len == 1 && *priority >= '0' && *priority <= '7')
p = *priority - '0';
}
}
- if (flags)
+ if (flags & OUTPUT_SHOW_ALL)
fprintf(f, ": %s%.*s%s\n", color_on, (int) message_len, message, color_off);
else if (!utf8_is_printable_n(message, message_len)) {
char bytes[FORMAT_BYTES_MAX];
} else if ((flags & OUTPUT_FULL_WIDTH) || (message_len + n + 1 < n_columns))
fprintf(f, ": %s%.*s%s\n", color_on, (int) message_len, message, color_off);
else if (n < n_columns && n_columns - n - 2 >= 3) {
- char *e;
+ char _cleanup_free_ *e;
e = ellipsize_mem(message, message_len, n_columns - n - 2, 90);
fprintf(f, ": %s%.*s%s\n", color_on, (int) message_len, message, color_off);
else
fprintf(f, ": %s%s%s\n", color_on, e, color_off);
-
- free(e);
} else
fputs("\n", f);
+ if (flags & OUTPUT_CATALOG)
+ print_catalog(f, j);
+
return 0;
}
const void *data;
size_t length;
- char *cursor;
+ char _cleanup_free_ *cursor = NULL;
uint64_t realtime;
char ts[FORMAT_TIMESTAMP_MAX];
int r;
assert(f);
assert(j);
+ sd_journal_set_data_threshold(j, 0);
+
r = sd_journal_get_realtime_usec(j, &realtime);
if (r < 0) {
log_error("Failed to get realtime timestamp: %s", strerror(-r));
format_timestamp(ts, sizeof(ts), realtime),
cursor);
- free(cursor);
-
SD_JOURNAL_FOREACH_DATA(j, data, length) {
if (!shall_print(data, length, flags)) {
const char *c;
fprintf(f, "\t%.*s\n", (int) length, (const char*) data);
}
+ if (flags & OUTPUT_CATALOG)
+ print_catalog(f, j);
+
return 0;
}
char sid[33];
int r;
usec_t realtime, monotonic;
- char *cursor;
+ char _cleanup_free_ *cursor = NULL;
const void *data;
size_t length;
assert(j);
+ sd_journal_set_data_threshold(j, 0);
+
r = sd_journal_get_realtime_usec(j, &realtime);
if (r < 0) {
log_error("Failed to get realtime timestamp: %s", strerror(-r));
(unsigned long long) monotonic,
sd_id128_to_string(boot_id, sid));
- free(cursor);
-
SD_JOURNAL_FOREACH_DATA(j, data, length) {
/* We already printed the boot id, from the data in
assert(f);
assert(p);
- if (!(flags & OUTPUT_SHOW_ALL) && l > JSON_THRESHOLD)
+ if (!(flags & OUTPUT_SHOW_ALL) && l >= JSON_THRESHOLD)
fputs("null", f);
OutputFlags flags) {
uint64_t realtime, monotonic;
- char *cursor;
+ char _cleanup_free_ *cursor = NULL;
const void *data;
size_t length;
sd_id128_t boot_id;
- char sid[33];
+ char sid[33], *k;
int r;
+ Hashmap *h = NULL;
+ bool done, separator;
assert(j);
+ sd_journal_set_data_threshold(j, flags & OUTPUT_SHOW_ALL ? 0 : JSON_THRESHOLD);
+
r = sd_journal_get_realtime_usec(j, &realtime);
if (r < 0) {
log_error("Failed to get realtime timestamp: %s", strerror(-r));
(unsigned long long) monotonic,
sd_id128_to_string(boot_id, sid));
}
- free(cursor);
+ h = hashmap_new(string_hash_func, string_compare_func);
+ if (!h)
+ return -ENOMEM;
+
+ /* First round, iterate through the entry and count how often each field appears */
SD_JOURNAL_FOREACH_DATA(j, data, length) {
- const char *c;
+ const char *eq;
+ char *n;
+ unsigned u;
- /* We already printed the boot id, from the data in
- * the header, hence let's suppress it here */
if (length >= 9 &&
memcmp(data, "_BOOT_ID=", 9) == 0)
continue;
- c = memchr(data, '=', length);
- if (!c) {
- log_error("Invalid field.");
- return -EINVAL;
- }
+ eq = memchr(data, '=', length);
+ if (!eq)
+ continue;
- if (mode == OUTPUT_JSON_PRETTY)
- fputs(",\n\t", f);
- else
- fputs(", ", f);
+ n = strndup(data, eq - (const char*) data);
+ if (!n) {
+ r = -ENOMEM;
+ goto finish;
+ }
- json_escape(f, data, c - (const char*) data, flags);
- fputs(" : ", f);
- json_escape(f, c + 1, length - (c - (const char*) data) - 1, flags);
+ u = PTR_TO_UINT(hashmap_get(h, n));
+ if (u == 0) {
+ r = hashmap_put(h, n, UINT_TO_PTR(1));
+ if (r < 0) {
+ free(n);
+ goto finish;
+ }
+ } else {
+ r = hashmap_update(h, n, UINT_TO_PTR(u + 1));
+ free(n);
+ if (r < 0)
+ goto finish;
+ }
}
+ separator = true;
+ do {
+ done = true;
+
+ SD_JOURNAL_FOREACH_DATA(j, data, length) {
+ const char *eq;
+ char *kk, *n;
+ size_t m;
+ unsigned u;
+
+ /* We already printed the boot id, from the data in
+ * the header, hence let's suppress it here */
+ if (length >= 9 &&
+ memcmp(data, "_BOOT_ID=", 9) == 0)
+ continue;
+
+ eq = memchr(data, '=', length);
+ if (!eq)
+ continue;
+
+ if (separator) {
+ if (mode == OUTPUT_JSON_PRETTY)
+ fputs(",\n\t", f);
+ else
+ fputs(", ", f);
+ }
+
+ m = eq - (const char*) data;
+
+ n = strndup(data, m);
+ if (!n) {
+ r = -ENOMEM;
+ goto finish;
+ }
+
+ u = PTR_TO_UINT(hashmap_get2(h, n, (void**) &kk));
+ if (u == 0) {
+ /* We already printed this, let's jump to the next */
+ free(n);
+ separator = false;
+
+ continue;
+ } else if (u == 1) {
+ /* Field only appears once, output it directly */
+
+ json_escape(f, data, m, flags);
+ fputs(" : ", f);
+
+ json_escape(f, eq + 1, length - m - 1, flags);
+
+ hashmap_remove(h, n);
+ free(kk);
+ free(n);
+
+ separator = true;
+
+ continue;
+
+ } else {
+ /* Field appears multiple times, output it as array */
+ json_escape(f, data, m, flags);
+ fputs(" : [ ", f);
+ json_escape(f, eq + 1, length - m - 1, flags);
+
+ /* Iterate through the end of the list */
+
+ while (sd_journal_enumerate_data(j, &data, &length) > 0) {
+ if (length < m + 1)
+ continue;
+
+ if (memcmp(data, n, m) != 0)
+ continue;
+
+ if (((const char*) data)[m] != '=')
+ continue;
+
+ fputs(", ", f);
+ json_escape(f, (const char*) data + m + 1, length - m - 1, flags);
+ }
+
+ fputs(" ]", f);
+
+ hashmap_remove(h, n);
+ free(kk);
+ free(n);
+
+ /* Iterate data fields form the beginning */
+ done = false;
+ separator = true;
+
+ break;
+ }
+ }
+
+ } while (!done);
+
if (mode == OUTPUT_JSON_PRETTY)
fputs("\n}\n", f);
else if (mode == OUTPUT_JSON_SSE)
else
fputs(" }\n", f);
- return 0;
+ r = 0;
+
+finish:
+ while ((k = hashmap_steal_first_key(h)))
+ free(k);
+
+ hashmap_free(h);
+
+ return r;
}
static int output_cat(
assert(j);
assert(f);
+ sd_journal_set_data_threshold(j, 0);
+
r = sd_journal_get_data(j, "MESSAGE", &data, &l);
if (r < 0) {
/* An entry without MESSAGE=? */