Skip to content

Commit cc25a67

Browse files
larskarlitskiyuwata
authored andcommitted
journalctl: add --output-fields= (systemd#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.
1 parent a8caf8b commit cc25a67

File tree

6 files changed

+122
-24
lines changed

6 files changed

+122
-24
lines changed

man/journalctl.xml

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -384,6 +384,20 @@
384384
</listitem>
385385
</varlistentry>
386386

387+
<varlistentry>
388+
<term><option>--output-fields=</option></term>
389+
390+
<listitem><para>A comma separated list of the fields which should
391+
be included in the output. This only has an effect for the output modes
392+
which would normally show all fields (<option>verbose</option>,
393+
<option>export</option>, <option>json</option>,
394+
<option>json-pretty</option>, and <option>json-sse</option>). The
395+
<literal>__CURSOR</literal>, <literal>__REALTIME_TIMESTAMP</literal>,
396+
<literal>__MONOTONIC_TIMESTAMP</literal>, and
397+
<literal>_BOOT_ID</literal> fields are always
398+
printed.</para></listitem>
399+
</varlistentry>
400+
387401
<varlistentry>
388402
<term><option>--utc</option></term>
389403

src/journal-remote/journal-gatewayd.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -225,7 +225,7 @@ static ssize_t request_reader_entries(
225225
return MHD_CONTENT_READER_END_WITH_ERROR;
226226
}
227227

228-
r = output_journal(m->tmp, m->journal, m->mode, 0, OUTPUT_FULL_WIDTH, NULL);
228+
r = output_journal(m->tmp, m->journal, m->mode, 0, OUTPUT_FULL_WIDTH, NULL, NULL);
229229
if (r < 0) {
230230
log_error_errno(r, "Failed to serialize item: %m");
231231
return MHD_CONTENT_READER_END_WITH_ERROR;

src/journal/journalctl.c

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -123,6 +123,7 @@ static const char *arg_machine = NULL;
123123
static uint64_t arg_vacuum_size = 0;
124124
static uint64_t arg_vacuum_n_files = 0;
125125
static usec_t arg_vacuum_time = 0;
126+
static char **arg_output_fields = NULL;
126127

127128
static enum {
128129
ACTION_SHOW,
@@ -377,6 +378,7 @@ static int parse_argv(int argc, char *argv[]) {
377378
ARG_VACUUM_FILES,
378379
ARG_VACUUM_TIME,
379380
ARG_NO_HOSTNAME,
381+
ARG_OUTPUT_FIELDS,
380382
};
381383

382384
static const struct option options[] = {
@@ -435,6 +437,7 @@ static int parse_argv(int argc, char *argv[]) {
435437
{ "vacuum-files", required_argument, NULL, ARG_VACUUM_FILES },
436438
{ "vacuum-time", required_argument, NULL, ARG_VACUUM_TIME },
437439
{ "no-hostname", no_argument, NULL, ARG_NO_HOSTNAME },
440+
{ "output-fields", required_argument, NULL, ARG_OUTPUT_FIELDS },
438441
{}
439442
};
440443

@@ -842,6 +845,24 @@ static int parse_argv(int argc, char *argv[]) {
842845
arg_action = ACTION_SYNC;
843846
break;
844847

848+
case ARG_OUTPUT_FIELDS: {
849+
_cleanup_strv_free_ char **v = NULL;
850+
851+
v = strv_split(optarg, ",");
852+
if (!v)
853+
return log_oom();
854+
855+
if (!arg_output_fields) {
856+
arg_output_fields = v;
857+
v = NULL;
858+
} else {
859+
r = strv_extend_strv(&arg_output_fields, v, true);
860+
if (r < 0)
861+
return log_oom();
862+
}
863+
break;
864+
}
865+
845866
case '?':
846867
return -EINVAL;
847868

@@ -2452,7 +2473,7 @@ int main(int argc, char *argv[]) {
24522473
arg_utc * OUTPUT_UTC |
24532474
arg_no_hostname * OUTPUT_NO_HOSTNAME;
24542475

2455-
r = output_journal(stdout, j, arg_output, 0, flags, &ellipsized);
2476+
r = output_journal(stdout, j, arg_output, 0, flags, arg_output_fields, &ellipsized);
24562477
need_seek = true;
24572478
if (r == -EADDRNOTAVAIL)
24582479
break;
@@ -2498,6 +2519,7 @@ int main(int argc, char *argv[]) {
24982519
strv_free(arg_syslog_identifier);
24992520
strv_free(arg_system_units);
25002521
strv_free(arg_user_units);
2522+
strv_free(arg_output_fields);
25012523

25022524
free(arg_root);
25032525
free(arg_verify_key);

src/shared/logs-show.c

Lines changed: 71 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,7 @@
4848
#include "stdio-util.h"
4949
#include "string-table.h"
5050
#include "string-util.h"
51+
#include "strv.h"
5152
#include "terminal-util.h"
5253
#include "time-util.h"
5354
#include "utf8.h"
@@ -136,6 +137,19 @@ static int parse_fieldv(const void *data, size_t length, const ParseFieldVec *fi
136137
return 0;
137138
}
138139

140+
static int field_set_test(Set *fields, const char *name, size_t n) {
141+
char *s = NULL;
142+
143+
if (!fields)
144+
return 1;
145+
146+
s = strndupa(name, n);
147+
if (!s)
148+
return log_oom();
149+
150+
return set_get(fields, s) ? 1 : 0;
151+
}
152+
139153
static bool shall_print(const char *p, size_t l, OutputFlags flags) {
140154
assert(p);
141155

@@ -353,7 +367,8 @@ static int output_short(
353367
sd_journal *j,
354368
OutputMode mode,
355369
unsigned n_columns,
356-
OutputFlags flags) {
370+
OutputFlags flags,
371+
Set *output_fields) {
357372

358373
int r;
359374
const void *data;
@@ -466,7 +481,8 @@ static int output_verbose(
466481
sd_journal *j,
467482
OutputMode mode,
468483
unsigned n_columns,
469-
OutputFlags flags) {
484+
OutputFlags flags,
485+
Set *output_fields) {
470486

471487
const void *data;
472488
size_t length;
@@ -527,6 +543,12 @@ static int output_verbose(
527543
}
528544
fieldlen = c - (const char*) data;
529545

546+
r = field_set_test(output_fields, data, fieldlen);
547+
if (r < 0)
548+
return r;
549+
if (!r)
550+
continue;
551+
530552
if (flags & OUTPUT_COLOR && startswith(data, "MESSAGE=")) {
531553
on = ANSI_HIGHLIGHT;
532554
off = ANSI_NORMAL;
@@ -564,7 +586,8 @@ static int output_export(
564586
sd_journal *j,
565587
OutputMode mode,
566588
unsigned n_columns,
567-
OutputFlags flags) {
589+
OutputFlags flags,
590+
Set *output_fields) {
568591

569592
sd_id128_t boot_id;
570593
char sid[33];
@@ -601,25 +624,31 @@ static int output_export(
601624
sd_id128_to_string(boot_id, sid));
602625

603626
JOURNAL_FOREACH_DATA_RETVAL(j, data, length, r) {
627+
const char *c;
604628

605629
/* We already printed the boot id, from the data in
606630
* the header, hence let's suppress it here */
607631
if (length >= 9 &&
608632
startswith(data, "_BOOT_ID="))
609633
continue;
610634

635+
c = memchr(data, '=', length);
636+
if (!c) {
637+
log_error("Invalid field.");
638+
return -EINVAL;
639+
}
640+
641+
r = field_set_test(output_fields, data, c - (const char *) data);
642+
if (r < 0)
643+
return r;
644+
if (!r)
645+
continue;
646+
611647
if (utf8_is_printable_newline(data, length, false))
612648
fwrite(data, length, 1, f);
613649
else {
614-
const char *c;
615650
uint64_t le64;
616651

617-
c = memchr(data, '=', length);
618-
if (!c) {
619-
log_error("Invalid field.");
620-
return -EINVAL;
621-
}
622-
623652
fwrite(data, c - (const char*) data, 1, f);
624653
fputc('\n', f);
625654
le64 = htole64(length - (c - (const char*) data) - 1);
@@ -695,7 +724,8 @@ static int output_json(
695724
sd_journal *j,
696725
OutputMode mode,
697726
unsigned n_columns,
698-
OutputFlags flags) {
727+
OutputFlags flags,
728+
Set *output_fields) {
699729

700730
uint64_t realtime, monotonic;
701731
_cleanup_free_ char *cursor = NULL;
@@ -814,13 +844,6 @@ static int output_json(
814844
if (!eq)
815845
continue;
816846

817-
if (separator) {
818-
if (mode == OUTPUT_JSON_PRETTY)
819-
fputs(",\n\t", f);
820-
else
821-
fputs(", ", f);
822-
}
823-
824847
m = eq - (const char*) data;
825848

826849
n = strndup(data, m);
@@ -829,6 +852,18 @@ static int output_json(
829852
goto finish;
830853
}
831854

855+
if (output_fields && !set_get(output_fields, n)) {
856+
free(n);
857+
continue;
858+
}
859+
860+
if (separator) {
861+
if (mode == OUTPUT_JSON_PRETTY)
862+
fputs(",\n\t", f);
863+
else
864+
fputs(", ", f);
865+
}
866+
832867
u = PTR_TO_UINT(hashmap_get2(h, n, (void**) &kk));
833868
if (u == 0) {
834869
/* We already printed this, let's jump to the next */
@@ -913,7 +948,8 @@ static int output_cat(
913948
sd_journal *j,
914949
OutputMode mode,
915950
unsigned n_columns,
916-
OutputFlags flags) {
951+
OutputFlags flags,
952+
Set *output_fields) {
917953

918954
const void *data;
919955
size_t l;
@@ -946,7 +982,8 @@ static int (*output_funcs[_OUTPUT_MODE_MAX])(
946982
sd_journal*j,
947983
OutputMode mode,
948984
unsigned n_columns,
949-
OutputFlags flags) = {
985+
OutputFlags flags,
986+
Set *output_fields) = {
950987

951988
[OUTPUT_SHORT] = output_short,
952989
[OUTPUT_SHORT_ISO] = output_short,
@@ -969,16 +1006,28 @@ int output_journal(
9691006
OutputMode mode,
9701007
unsigned n_columns,
9711008
OutputFlags flags,
1009+
char **output_fields,
9721010
bool *ellipsized) {
9731011

9741012
int ret;
1013+
_cleanup_set_free_free_ Set *fields = NULL;
9751014
assert(mode >= 0);
9761015
assert(mode < _OUTPUT_MODE_MAX);
9771016

9781017
if (n_columns <= 0)
9791018
n_columns = columns();
9801019

981-
ret = output_funcs[mode](f, j, mode, n_columns, flags);
1020+
if (output_fields) {
1021+
fields = set_new(&string_hash_ops);
1022+
if (!fields)
1023+
return log_oom();
1024+
1025+
ret = set_put_strdupv(fields, output_fields);
1026+
if (ret < 0)
1027+
return ret;
1028+
}
1029+
1030+
ret = output_funcs[mode](f, j, mode, n_columns, flags, fields);
9821031

9831032
if (ellipsized && ret > 0)
9841033
*ellipsized = true;
@@ -1060,7 +1109,7 @@ static int show_journal(FILE *f,
10601109
line++;
10611110
maybe_print_begin_newline(f, &flags);
10621111

1063-
r = output_journal(f, j, mode, n_columns, flags, ellipsized);
1112+
r = output_journal(f, j, mode, n_columns, flags, NULL, ellipsized);
10641113
if (r < 0)
10651114
return r;
10661115
}

src/shared/logs-show.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ int output_journal(
3737
OutputMode mode,
3838
unsigned n_columns,
3939
OutputFlags flags,
40+
char **output_fields,
4041
bool *ellipsized);
4142

4243
int add_match_this_boot(sd_journal *j, const char *machine);

test/TEST-04-JOURNAL/test-journal.sh

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,18 @@ journalctl --sync
5151
journalctl -b -o cat -t "$ID" >/output
5252
cmp /expected /output
5353

54+
# --output-fields restricts output
55+
ID=$(journalctl --new-id128 | sed -n 2p)
56+
printf $'foo' | systemd-cat -t "$ID" --level-prefix false
57+
journalctl --sync
58+
journalctl -b -o export --output-fields=MESSAGE,FOO --output-fields=PRIORITY,MESSAGE -t "$ID" >/output
59+
[[ `grep -c . /output` -eq 6 ]]
60+
grep -q '^__CURSOR=' /output
61+
grep -q '^MESSAGE=foo$' /output
62+
grep -q '^PRIORITY=6$' /output
63+
! grep -q '^FOO=' /output
64+
! grep -q '^SYSLOG_FACILITY=' /output
65+
5466
# Don't lose streams on restart
5567
systemctl start forever-print-hola
5668
sleep 3

0 commit comments

Comments
 (0)