summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorLars Karlitski <lars@karlitski.net>2017-10-27 05:10:47 +0200
committerYu Watanabe <watanabe.yu+github@gmail.com>2017-10-27 05:10:47 +0200
commitcc25a67e2af21833f9fef5aba6e16beafa03b0c7 (patch)
tree404ff13bb957e8b462862c465e01d23b9bfe72be
parentMerge pull request #7066 from poettering/specifier-update (diff)
downloadsystemd-cc25a67e2af21833f9fef5aba6e16beafa03b0c7.tar.xz
systemd-cc25a67e2af21833f9fef5aba6e16beafa03b0c7.zip
journalctl: add --output-fields= (#7181)
This option allows restricting the shown fields in the output modes that would normally show all fields. It allows clients that are only interested in a subset of the fields to access those more efficiently. Also, it makes the resulting size of the output more predictable. It has no effect on the various `short` output modes, because those already only show a subset of the fields.
-rw-r--r--man/journalctl.xml14
-rw-r--r--src/journal-remote/journal-gatewayd.c2
-rw-r--r--src/journal/journalctl.c24
-rw-r--r--src/shared/logs-show.c93
-rw-r--r--src/shared/logs-show.h1
-rwxr-xr-xtest/TEST-04-JOURNAL/test-journal.sh12
6 files changed, 122 insertions, 24 deletions
diff --git a/man/journalctl.xml b/man/journalctl.xml
index 444b916073..c80e0ed1c2 100644
--- a/man/journalctl.xml
+++ b/man/journalctl.xml
@@ -385,6 +385,20 @@
</varlistentry>
<varlistentry>
+ <term><option>--output-fields=</option></term>
+
+ <listitem><para>A comma separated list of the fields which should
+ be included in the output. This only has an effect for the output modes
+ which would normally show all fields (<option>verbose</option>,
+ <option>export</option>, <option>json</option>,
+ <option>json-pretty</option>, and <option>json-sse</option>). The
+ <literal>__CURSOR</literal>, <literal>__REALTIME_TIMESTAMP</literal>,
+ <literal>__MONOTONIC_TIMESTAMP</literal>, and
+ <literal>_BOOT_ID</literal> fields are always
+ printed.</para></listitem>
+ </varlistentry>
+
+ <varlistentry>
<term><option>--utc</option></term>
<listitem><para>Express time in Coordinated Universal Time
diff --git a/src/journal-remote/journal-gatewayd.c b/src/journal-remote/journal-gatewayd.c
index a0f1f3e926..fec16f7c7c 100644
--- a/src/journal-remote/journal-gatewayd.c
+++ b/src/journal-remote/journal-gatewayd.c
@@ -225,7 +225,7 @@ static ssize_t request_reader_entries(
return MHD_CONTENT_READER_END_WITH_ERROR;
}
- r = output_journal(m->tmp, m->journal, m->mode, 0, OUTPUT_FULL_WIDTH, NULL);
+ r = output_journal(m->tmp, m->journal, m->mode, 0, OUTPUT_FULL_WIDTH, NULL, NULL);
if (r < 0) {
log_error_errno(r, "Failed to serialize item: %m");
return MHD_CONTENT_READER_END_WITH_ERROR;
diff --git a/src/journal/journalctl.c b/src/journal/journalctl.c
index e826fa631e..88c1ac6dd0 100644
--- a/src/journal/journalctl.c
+++ b/src/journal/journalctl.c
@@ -123,6 +123,7 @@ static const char *arg_machine = NULL;
static uint64_t arg_vacuum_size = 0;
static uint64_t arg_vacuum_n_files = 0;
static usec_t arg_vacuum_time = 0;
+static char **arg_output_fields = NULL;
static enum {
ACTION_SHOW,
@@ -377,6 +378,7 @@ static int parse_argv(int argc, char *argv[]) {
ARG_VACUUM_FILES,
ARG_VACUUM_TIME,
ARG_NO_HOSTNAME,
+ ARG_OUTPUT_FIELDS,
};
static const struct option options[] = {
@@ -435,6 +437,7 @@ static int parse_argv(int argc, char *argv[]) {
{ "vacuum-files", required_argument, NULL, ARG_VACUUM_FILES },
{ "vacuum-time", required_argument, NULL, ARG_VACUUM_TIME },
{ "no-hostname", no_argument, NULL, ARG_NO_HOSTNAME },
+ { "output-fields", required_argument, NULL, ARG_OUTPUT_FIELDS },
{}
};
@@ -842,6 +845,24 @@ static int parse_argv(int argc, char *argv[]) {
arg_action = ACTION_SYNC;
break;
+ case ARG_OUTPUT_FIELDS: {
+ _cleanup_strv_free_ char **v = NULL;
+
+ v = strv_split(optarg, ",");
+ if (!v)
+ return log_oom();
+
+ if (!arg_output_fields) {
+ arg_output_fields = v;
+ v = NULL;
+ } else {
+ r = strv_extend_strv(&arg_output_fields, v, true);
+ if (r < 0)
+ return log_oom();
+ }
+ break;
+ }
+
case '?':
return -EINVAL;
@@ -2452,7 +2473,7 @@ int main(int argc, char *argv[]) {
arg_utc * OUTPUT_UTC |
arg_no_hostname * OUTPUT_NO_HOSTNAME;
- r = output_journal(stdout, j, arg_output, 0, flags, &ellipsized);
+ r = output_journal(stdout, j, arg_output, 0, flags, arg_output_fields, &ellipsized);
need_seek = true;
if (r == -EADDRNOTAVAIL)
break;
@@ -2498,6 +2519,7 @@ finish:
strv_free(arg_syslog_identifier);
strv_free(arg_system_units);
strv_free(arg_user_units);
+ strv_free(arg_output_fields);
free(arg_root);
free(arg_verify_key);
diff --git a/src/shared/logs-show.c b/src/shared/logs-show.c
index a6b6859582..5e0d59f5a2 100644
--- a/src/shared/logs-show.c
+++ b/src/shared/logs-show.c
@@ -48,6 +48,7 @@
#include "stdio-util.h"
#include "string-table.h"
#include "string-util.h"
+#include "strv.h"
#include "terminal-util.h"
#include "time-util.h"
#include "utf8.h"
@@ -136,6 +137,19 @@ static int parse_fieldv(const void *data, size_t length, const ParseFieldVec *fi
return 0;
}
+static int field_set_test(Set *fields, const char *name, size_t n) {
+ char *s = NULL;
+
+ if (!fields)
+ return 1;
+
+ s = strndupa(name, n);
+ if (!s)
+ return log_oom();
+
+ return set_get(fields, s) ? 1 : 0;
+}
+
static bool shall_print(const char *p, size_t l, OutputFlags flags) {
assert(p);
@@ -353,7 +367,8 @@ static int output_short(
sd_journal *j,
OutputMode mode,
unsigned n_columns,
- OutputFlags flags) {
+ OutputFlags flags,
+ Set *output_fields) {
int r;
const void *data;
@@ -466,7 +481,8 @@ static int output_verbose(
sd_journal *j,
OutputMode mode,
unsigned n_columns,
- OutputFlags flags) {
+ OutputFlags flags,
+ Set *output_fields) {
const void *data;
size_t length;
@@ -527,6 +543,12 @@ static int output_verbose(
}
fieldlen = c - (const char*) data;
+ r = field_set_test(output_fields, data, fieldlen);
+ if (r < 0)
+ return r;
+ if (!r)
+ continue;
+
if (flags & OUTPUT_COLOR && startswith(data, "MESSAGE=")) {
on = ANSI_HIGHLIGHT;
off = ANSI_NORMAL;
@@ -564,7 +586,8 @@ static int output_export(
sd_journal *j,
OutputMode mode,
unsigned n_columns,
- OutputFlags flags) {
+ OutputFlags flags,
+ Set *output_fields) {
sd_id128_t boot_id;
char sid[33];
@@ -601,6 +624,7 @@ static int output_export(
sd_id128_to_string(boot_id, sid));
JOURNAL_FOREACH_DATA_RETVAL(j, data, length, r) {
+ const char *c;
/* We already printed the boot id, from the data in
* the header, hence let's suppress it here */
@@ -608,18 +632,23 @@ static int output_export(
startswith(data, "_BOOT_ID="))
continue;
+ c = memchr(data, '=', length);
+ if (!c) {
+ log_error("Invalid field.");
+ return -EINVAL;
+ }
+
+ r = field_set_test(output_fields, data, c - (const char *) data);
+ if (r < 0)
+ return r;
+ if (!r)
+ continue;
+
if (utf8_is_printable_newline(data, length, false))
fwrite(data, length, 1, f);
else {
- 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, f);
fputc('\n', f);
le64 = htole64(length - (c - (const char*) data) - 1);
@@ -695,7 +724,8 @@ static int output_json(
sd_journal *j,
OutputMode mode,
unsigned n_columns,
- OutputFlags flags) {
+ OutputFlags flags,
+ Set *output_fields) {
uint64_t realtime, monotonic;
_cleanup_free_ char *cursor = NULL;
@@ -814,13 +844,6 @@ static int output_json(
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);
@@ -829,6 +852,18 @@ static int output_json(
goto finish;
}
+ if (output_fields && !set_get(output_fields, n)) {
+ free(n);
+ continue;
+ }
+
+ if (separator) {
+ if (mode == OUTPUT_JSON_PRETTY)
+ fputs(",\n\t", f);
+ else
+ fputs(", ", f);
+ }
+
u = PTR_TO_UINT(hashmap_get2(h, n, (void**) &kk));
if (u == 0) {
/* We already printed this, let's jump to the next */
@@ -913,7 +948,8 @@ static int output_cat(
sd_journal *j,
OutputMode mode,
unsigned n_columns,
- OutputFlags flags) {
+ OutputFlags flags,
+ Set *output_fields) {
const void *data;
size_t l;
@@ -946,7 +982,8 @@ static int (*output_funcs[_OUTPUT_MODE_MAX])(
sd_journal*j,
OutputMode mode,
unsigned n_columns,
- OutputFlags flags) = {
+ OutputFlags flags,
+ Set *output_fields) = {
[OUTPUT_SHORT] = output_short,
[OUTPUT_SHORT_ISO] = output_short,
@@ -969,16 +1006,28 @@ int output_journal(
OutputMode mode,
unsigned n_columns,
OutputFlags flags,
+ char **output_fields,
bool *ellipsized) {
int ret;
+ _cleanup_set_free_free_ Set *fields = NULL;
assert(mode >= 0);
assert(mode < _OUTPUT_MODE_MAX);
if (n_columns <= 0)
n_columns = columns();
- ret = output_funcs[mode](f, j, mode, n_columns, flags);
+ if (output_fields) {
+ fields = set_new(&string_hash_ops);
+ if (!fields)
+ return log_oom();
+
+ ret = set_put_strdupv(fields, output_fields);
+ if (ret < 0)
+ return ret;
+ }
+
+ ret = output_funcs[mode](f, j, mode, n_columns, flags, fields);
if (ellipsized && ret > 0)
*ellipsized = true;
@@ -1060,7 +1109,7 @@ static int show_journal(FILE *f,
line++;
maybe_print_begin_newline(f, &flags);
- r = output_journal(f, j, mode, n_columns, flags, ellipsized);
+ r = output_journal(f, j, mode, n_columns, flags, NULL, ellipsized);
if (r < 0)
return r;
}
diff --git a/src/shared/logs-show.h b/src/shared/logs-show.h
index 6643440881..3d583b79ef 100644
--- a/src/shared/logs-show.h
+++ b/src/shared/logs-show.h
@@ -37,6 +37,7 @@ int output_journal(
OutputMode mode,
unsigned n_columns,
OutputFlags flags,
+ char **output_fields,
bool *ellipsized);
int add_match_this_boot(sd_journal *j, const char *machine);
diff --git a/test/TEST-04-JOURNAL/test-journal.sh b/test/TEST-04-JOURNAL/test-journal.sh
index 493ff00ce0..260cae09ab 100755
--- a/test/TEST-04-JOURNAL/test-journal.sh
+++ b/test/TEST-04-JOURNAL/test-journal.sh
@@ -51,6 +51,18 @@ journalctl --sync
journalctl -b -o cat -t "$ID" >/output
cmp /expected /output
+# --output-fields restricts output
+ID=$(journalctl --new-id128 | sed -n 2p)
+printf $'foo' | systemd-cat -t "$ID" --level-prefix false
+journalctl --sync
+journalctl -b -o export --output-fields=MESSAGE,FOO --output-fields=PRIORITY,MESSAGE -t "$ID" >/output
+[[ `grep -c . /output` -eq 6 ]]
+grep -q '^__CURSOR=' /output
+grep -q '^MESSAGE=foo$' /output
+grep -q '^PRIORITY=6$' /output
+! grep -q '^FOO=' /output
+! grep -q '^SYSLOG_FACILITY=' /output
+
# Don't lose streams on restart
systemctl start forever-print-hola
sleep 3