mirror of
https://github.com/systemd/systemd.git
synced 2025-01-12 13:18:14 +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 <unistd.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <sys/poll.h>
|
#include <sys/poll.h>
|
||||||
|
#include <time.h>
|
||||||
|
|
||||||
#include "sd-journal.h"
|
#include "sd-journal.h"
|
||||||
#include "log.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 main(int argc, char *argv[]) {
|
||||||
int r, i, fd;
|
int r, i, fd;
|
||||||
sd_journal *j = NULL;
|
sd_journal *j = NULL;
|
||||||
|
unsigned line = 0;
|
||||||
|
|
||||||
log_set_max_level(LOG_DEBUG);
|
log_set_max_level(LOG_DEBUG);
|
||||||
log_set_target(LOG_TARGET_CONSOLE);
|
log_set_target(LOG_TARGET_CONSOLE);
|
||||||
@ -69,33 +382,18 @@ int main(int argc, char *argv[]) {
|
|||||||
goto finish;
|
goto finish;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (arg_output == OUTPUT_JSON)
|
||||||
|
fputc('[', stdout);
|
||||||
|
|
||||||
for (;;) {
|
for (;;) {
|
||||||
struct pollfd pollfd;
|
struct pollfd pollfd;
|
||||||
|
|
||||||
while (sd_journal_next(j) > 0) {
|
while (sd_journal_next(j) > 0) {
|
||||||
const void *data;
|
line ++;
|
||||||
size_t length;
|
|
||||||
char *cursor;
|
|
||||||
uint64_t realtime = 0, monotonic = 0;
|
|
||||||
|
|
||||||
r = sd_journal_get_cursor(j, &cursor);
|
r = output_funcs[arg_output](j, line);
|
||||||
if (r < 0) {
|
if (r < 0)
|
||||||
log_error("Failed to get cursor: %s", strerror(-r));
|
|
||||||
goto finish;
|
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)
|
if (!arg_follow)
|
||||||
@ -121,6 +419,9 @@ int main(int argc, char *argv[]) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (arg_output == OUTPUT_JSON)
|
||||||
|
fputs("\n]\n", stdout);
|
||||||
|
|
||||||
finish:
|
finish:
|
||||||
if (j)
|
if (j)
|
||||||
sd_journal_close(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;
|
return r;
|
||||||
}
|
}
|
||||||
|
|
||||||
journal_file_dump(f);
|
/* journal_file_dump(f); */
|
||||||
|
|
||||||
r = hashmap_put(j->files, f->path, f);
|
r = hashmap_put(j->files, f->path, f);
|
||||||
if (r < 0) {
|
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;
|
return r;
|
||||||
}
|
}
|
||||||
|
|
||||||
int columns(void) {
|
unsigned columns(void) {
|
||||||
static __thread int parsed_columns = 0;
|
static __thread int parsed_columns = 0;
|
||||||
const char *e;
|
const char *e;
|
||||||
|
|
||||||
@ -3948,38 +3948,41 @@ int running_in_chroot(void) {
|
|||||||
a.st_ino != b.st_ino;
|
a.st_ino != b.st_ino;
|
||||||
}
|
}
|
||||||
|
|
||||||
char *ellipsize(const char *s, unsigned length, unsigned percent) {
|
char *ellipsize_mem(const char *s, size_t old_length, size_t new_length, unsigned percent) {
|
||||||
size_t l, x;
|
size_t x;
|
||||||
char *r;
|
char *r;
|
||||||
|
|
||||||
assert(s);
|
assert(s);
|
||||||
assert(percent <= 100);
|
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)
|
r = new0(char, new_length+1);
|
||||||
return strdup(s);
|
if (!r)
|
||||||
|
|
||||||
if (!(r = new0(char, length+1)))
|
|
||||||
return r;
|
return r;
|
||||||
|
|
||||||
x = (length * percent) / 100;
|
x = (new_length * percent) / 100;
|
||||||
|
|
||||||
if (x > length - 3)
|
if (x > new_length - 3)
|
||||||
x = length - 3;
|
x = new_length - 3;
|
||||||
|
|
||||||
memcpy(r, s, x);
|
memcpy(r, s, x);
|
||||||
r[x] = '.';
|
r[x] = '.';
|
||||||
r[x+1] = '.';
|
r[x+1] = '.';
|
||||||
r[x+2] = '.';
|
r[x+2] = '.';
|
||||||
memcpy(r + x + 3,
|
memcpy(r + x + 3,
|
||||||
s + l - (length - x - 3),
|
s + old_length - (new_length - x - 3),
|
||||||
length - x - 3);
|
new_length - x - 3);
|
||||||
|
|
||||||
return r;
|
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 touch(const char *path) {
|
||||||
int fd;
|
int fd;
|
||||||
|
|
||||||
|
@ -378,11 +378,12 @@ void status_vprintf(const char *format, va_list ap);
|
|||||||
void status_printf(const char *format, ...);
|
void status_printf(const char *format, ...);
|
||||||
void status_welcome(void);
|
void status_welcome(void);
|
||||||
|
|
||||||
int columns(void);
|
unsigned columns(void);
|
||||||
|
|
||||||
int running_in_chroot(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);
|
int touch(const char *path);
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user