mirror of
https://github.com/systemd/systemd.git
synced 2024-12-23 21:35:11 +03:00
journalctl: add json, export, short and verbose output modes
This commit is contained in:
parent
807e17f05e
commit
72f597065c
@ -27,15 +27,328 @@
|
||||
#include <unistd.h>
|
||||
#include <stdlib.h>
|
||||
#include <sys/poll.h>
|
||||
#include <time.h>
|
||||
|
||||
#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;
|
||||
line ++;
|
||||
|
||||
r = sd_journal_get_cursor(j, &cursor);
|
||||
if (r < 0) {
|
||||
log_error("Failed to get cursor: %s", strerror(-r));
|
||||
r = output_funcs[arg_output](j, line);
|
||||
if (r < 0)
|
||||
goto finish;
|
||||
}
|
||||
|
||||
printf("entry: %s\n", cursor);
|
||||
free(cursor);
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
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);
|
||||
|
@ -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) {
|
||||
|
31
src/util.c
31
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;
|
||||
|
||||
|
@ -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);
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user