]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
journalctl: add --output-fields= (#7181)
authorLars Karlitski <lars@karlitski.net>
Fri, 27 Oct 2017 03:10:47 +0000 (05:10 +0200)
committerYu Watanabe <watanabe.yu+github@gmail.com>
Fri, 27 Oct 2017 03:10:47 +0000 (12:10 +0900)
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.

man/journalctl.xml
src/journal-remote/journal-gatewayd.c
src/journal/journalctl.c
src/shared/logs-show.c
src/shared/logs-show.h
test/TEST-04-JOURNAL/test-journal.sh

index 444b9160735043155643e0e5d5d153fd2e1aae97..c80e0ed1c20b492c026488b475f7f64f732be63f 100644 (file)
         </listitem>
       </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>
 
index a0f1f3e926b3b83c6b0065108e1df32c80156d49..fec16f7c7c4c9440519eb59443cd69942c08947e 100644 (file)
@@ -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;
index e826fa631e4a977ff831eb9bf864e68c8e9307d1..88c1ac6dd02bda06e17e33df09f50f4e378d8ad4 100644 (file)
@@ -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);
index a6b68595822628d8a03e5528fdce7378f4fcc97c..5e0d59f5a25afe8da726da82cee6a6d15e183851 100644 (file)
@@ -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;
                 }
index 66434408812d62bdca81843cb25c16d6320d882e..3d583b79efddee50d59ebd1638e7065d10f31ee0 100644 (file)
@@ -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);
index 493ff00ce00801364bdcc1592bffd91e4001b131..260cae09ab14373382c8193af724a0e01e363b7f 100755 (executable)
@@ -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