1 /*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
4 This file is part of systemd.
6 Copyright 2012 Lennart Poettering
8 systemd is free software; you can redistribute it and/or modify it
9 under the terms of the GNU Lesser General Public License as published by
10 the Free Software Foundation; either version 2.1 of the License, or
11 (at your option) any later version.
13 systemd is distributed in the hope that it will be useful, but
14 WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 Lesser General Public License for more details.
18 You should have received a copy of the GNU Lesser General Public License
19 along with systemd; If not, see <http://www.gnu.org/licenses/>.
28 #include "logs-show.h"
33 #include "journal-internal.h"
35 /* up to three lines (each up to 100 characters),
36 or 300 characters, whichever is less */
37 #define PRINT_LINE_THRESHOLD 3
38 #define PRINT_CHAR_THRESHOLD 300
40 #define JSON_THRESHOLD 4096
42 static int print_catalog(FILE *f, sd_journal *j) {
44 _cleanup_free_ char *t = NULL, *z = NULL;
47 r = sd_journal_get_catalog(j, &t);
51 z = strreplace(strstrip(t), "\n", "\n-- ");
62 static int parse_field(const void *data, size_t length, const char *field, char **target, size_t *target_size) {
75 if (memcmp(data, field, fl))
83 memcpy(buf, (const char*) data + fl, nl);
93 static bool shall_print(const char *p, size_t l, OutputFlags flags) {
96 if (flags & OUTPUT_SHOW_ALL)
99 if (l >= PRINT_CHAR_THRESHOLD)
102 if (!utf8_is_printable(p, l))
108 static bool print_multiline(FILE *f, unsigned prefix, unsigned n_columns, OutputMode flags, int priority, const char* message, size_t message_len) {
109 const char *color_on = "", *color_off = "";
110 const char *pos, *end;
111 bool ellipsized = false;
114 if (flags & OUTPUT_COLOR) {
115 if (priority <= LOG_ERR) {
116 color_on = ANSI_HIGHLIGHT_RED_ON;
117 color_off = ANSI_HIGHLIGHT_OFF;
118 } else if (priority <= LOG_NOTICE) {
119 color_on = ANSI_HIGHLIGHT_ON;
120 color_off = ANSI_HIGHLIGHT_OFF;
125 pos < message + message_len;
126 pos = end + 1, line++) {
127 bool continuation = line > 0;
130 for (end = pos; end < message + message_len && *end != '\n'; end++)
135 /* We need to figure out when we are showing the last line, and
136 * will skip subsequent lines. In that case, we will put the dots
137 * at the end of the line, instead of putting dots in the middle
141 line + 1 == PRINT_LINE_THRESHOLD ||
142 end + 1 >= message + message_len;
144 if (flags & (OUTPUT_FULL_WIDTH | OUTPUT_SHOW_ALL) ||
145 (prefix + len + 1 < n_columns && !tail_line)) {
146 fprintf(f, "%*s%s%.*s%s\n",
147 continuation * prefix, "",
148 color_on, len, pos, color_off);
152 /* Beyond this point, ellipsization will happen. */
155 if (prefix < n_columns && n_columns - prefix >= 3) {
156 if (n_columns - prefix > (unsigned) len + 3)
157 fprintf(f, "%*s%s%.*s...%s\n",
158 continuation * prefix, "",
159 color_on, len, pos, color_off);
161 _cleanup_free_ char *e;
163 e = ellipsize_mem(pos, len, n_columns - prefix,
164 tail_line ? 100 : 90);
166 fprintf(f, "%*s%s%.*s%s\n",
167 continuation * prefix, "",
168 color_on, len, pos, color_off);
170 fprintf(f, "%*s%s%s%s\n",
171 continuation * prefix, "",
172 color_on, e, color_off);
184 static int output_short(
195 _cleanup_free_ char *hostname = NULL, *identifier = NULL, *comm = NULL, *pid = NULL, *fake_pid = NULL, *message = NULL, *realtime = NULL, *monotonic = NULL, *priority = NULL;
196 size_t hostname_len = 0, identifier_len = 0, comm_len = 0, pid_len = 0, fake_pid_len = 0, message_len = 0, realtime_len = 0, monotonic_len = 0, priority_len = 0;
198 bool ellipsized = false;
203 /* Set the threshold to one bigger than the actual print
204 * treshold, so that if the line is actually longer than what
205 * we're willing to print, ellipsization will occur. This way
206 * we won't output a misleading line without any indication of
209 sd_journal_set_data_threshold(j, flags & (OUTPUT_SHOW_ALL|OUTPUT_FULL_WIDTH) ? 0 : PRINT_CHAR_THRESHOLD + 1);
211 JOURNAL_FOREACH_DATA_RETVAL(j, data, length, r) {
213 r = parse_field(data, length, "PRIORITY=", &priority, &priority_len);
219 r = parse_field(data, length, "_HOSTNAME=", &hostname, &hostname_len);
225 r = parse_field(data, length, "SYSLOG_IDENTIFIER=", &identifier, &identifier_len);
231 r = parse_field(data, length, "_COMM=", &comm, &comm_len);
237 r = parse_field(data, length, "_PID=", &pid, &pid_len);
243 r = parse_field(data, length, "SYSLOG_PID=", &fake_pid, &fake_pid_len);
249 r = parse_field(data, length, "_SOURCE_REALTIME_TIMESTAMP=", &realtime, &realtime_len);
255 r = parse_field(data, length, "_SOURCE_MONOTONIC_TIMESTAMP=", &monotonic, &monotonic_len);
261 r = parse_field(data, length, "MESSAGE=", &message, &message_len);
272 if (!(flags & OUTPUT_SHOW_ALL))
273 strip_tab_ansi(&message, &message_len);
275 if (priority_len == 1 && *priority >= '0' && *priority <= '7')
278 if (mode == OUTPUT_SHORT_MONOTONIC) {
285 r = safe_atou64(monotonic, &t);
288 r = sd_journal_get_monotonic_usec(j, &t, &boot_id);
291 log_error("Failed to get monotonic timestamp: %s", strerror(-r));
295 fprintf(f, "[%5llu.%06llu]",
296 (unsigned long long) (t / USEC_PER_SEC),
297 (unsigned long long) (t % USEC_PER_SEC));
299 n += 1 + 5 + 1 + 6 + 1;
310 r = safe_atou64(realtime, &x);
313 r = sd_journal_get_realtime_usec(j, &x);
316 log_error("Failed to get realtime timestamp: %s", strerror(-r));
320 t = (time_t) (x / USEC_PER_SEC);
321 if (mode == OUTPUT_SHORT_ISO)
322 r = strftime(buf, sizeof(buf), "%Y-%m-%dT%H:%M:%S%z", localtime_r(&t, &tm));
324 r = strftime(buf, sizeof(buf), "%b %d %H:%M:%S", localtime_r(&t, &tm));
327 log_error("Failed to format time.");
335 if (hostname && shall_print(hostname, hostname_len, flags)) {
336 fprintf(f, " %.*s", (int) hostname_len, hostname);
337 n += hostname_len + 1;
340 if (identifier && shall_print(identifier, identifier_len, flags)) {
341 fprintf(f, " %.*s", (int) identifier_len, identifier);
342 n += identifier_len + 1;
343 } else if (comm && shall_print(comm, comm_len, flags)) {
344 fprintf(f, " %.*s", (int) comm_len, comm);
349 if (pid && shall_print(pid, pid_len, flags)) {
350 fprintf(f, "[%.*s]", (int) pid_len, pid);
352 } else if (fake_pid && shall_print(fake_pid, fake_pid_len, flags)) {
353 fprintf(f, "[%.*s]", (int) fake_pid_len, fake_pid);
354 n += fake_pid_len + 2;
357 if (!(flags & OUTPUT_SHOW_ALL) && !utf8_is_printable(message, message_len)) {
358 char bytes[FORMAT_BYTES_MAX];
359 fprintf(f, ": [%s blob data]\n", format_bytes(bytes, sizeof(bytes), message_len));
363 print_multiline(f, n + 2, n_columns, flags, p, message, message_len);
366 if (flags & OUTPUT_CATALOG)
372 static int output_verbose(
381 _cleanup_free_ char *cursor = NULL;
383 char ts[FORMAT_TIMESTAMP_MAX];
389 sd_journal_set_data_threshold(j, 0);
391 r = sd_journal_get_realtime_usec(j, &realtime);
393 log_full(r == -EADDRNOTAVAIL ? LOG_DEBUG : LOG_ERR,
394 "Failed to get realtime timestamp: %s", strerror(-r));
398 r = sd_journal_get_cursor(j, &cursor);
400 log_error("Failed to get cursor: %s", strerror(-r));
404 fprintf(f, "%s [%s]\n",
405 format_timestamp(ts, sizeof(ts), realtime),
408 JOURNAL_FOREACH_DATA_RETVAL(j, data, length, r) {
411 const char *on = "", *off = "";
413 c = memchr(data, '=', length);
415 log_error("Invalid field.");
418 fieldlen = c - (const char*) data;
420 if (flags & OUTPUT_COLOR && startswith(data, "MESSAGE=")) {
421 on = ANSI_HIGHLIGHT_ON;
422 off = ANSI_HIGHLIGHT_OFF;
425 if (flags & OUTPUT_SHOW_ALL ||
426 (((length < PRINT_CHAR_THRESHOLD) || flags & OUTPUT_FULL_WIDTH)
427 && utf8_is_printable(data, length))) {
428 fprintf(f, " %s%.*s=", on, fieldlen, (const char*)data);
429 print_multiline(f, 4 + fieldlen + 1, 0, OUTPUT_FULL_WIDTH, 0, c + 1, length - fieldlen - 1);
432 char bytes[FORMAT_BYTES_MAX];
434 fprintf(f, " %s%.*s=[%s blob data]%s\n",
436 (int) (c - (const char*) data),
438 format_bytes(bytes, sizeof(bytes), length - (c - (const char *) data) - 1),
446 if (flags & OUTPUT_CATALOG)
452 static int output_export(
462 usec_t realtime, monotonic;
463 _cleanup_free_ char *cursor = NULL;
469 sd_journal_set_data_threshold(j, 0);
471 r = sd_journal_get_realtime_usec(j, &realtime);
473 log_error("Failed to get realtime timestamp: %s", strerror(-r));
477 r = sd_journal_get_monotonic_usec(j, &monotonic, &boot_id);
479 log_error("Failed to get monotonic timestamp: %s", strerror(-r));
483 r = sd_journal_get_cursor(j, &cursor);
485 log_error("Failed to get cursor: %s", strerror(-r));
491 "__REALTIME_TIMESTAMP=%llu\n"
492 "__MONOTONIC_TIMESTAMP=%llu\n"
495 (unsigned long long) realtime,
496 (unsigned long long) monotonic,
497 sd_id128_to_string(boot_id, sid));
499 JOURNAL_FOREACH_DATA_RETVAL(j, data, length, r) {
501 /* We already printed the boot id, from the data in
502 * the header, hence let's suppress it here */
504 hasprefix(data, "_BOOT_ID="))
507 if (!utf8_is_printable(data, length)) {
511 c = memchr(data, '=', length);
513 log_error("Invalid field.");
517 fwrite(data, c - (const char*) data, 1, f);
519 le64 = htole64(length - (c - (const char*) data) - 1);
520 fwrite(&le64, sizeof(le64), 1, f);
521 fwrite(c + 1, length - (c - (const char*) data) - 1, 1, f);
523 fwrite(data, length, 1, f);
545 if (!(flags & OUTPUT_SHOW_ALL) && l >= JSON_THRESHOLD)
549 else if (!utf8_is_printable(p, l)) {
550 bool not_first = false;
556 fprintf(f, ", %u", (uint8_t) *p);
559 fprintf(f, "%u", (uint8_t) *p);
571 if (*p == '"' || *p == '\\') {
574 } else if (*p == '\n')
577 fprintf(f, "\\u%04x", *p);
589 static int output_json(
596 uint64_t realtime, monotonic;
597 _cleanup_free_ char *cursor = NULL;
604 bool done, separator;
608 sd_journal_set_data_threshold(j, flags & OUTPUT_SHOW_ALL ? 0 : JSON_THRESHOLD);
610 r = sd_journal_get_realtime_usec(j, &realtime);
612 log_error("Failed to get realtime timestamp: %s", strerror(-r));
616 r = sd_journal_get_monotonic_usec(j, &monotonic, &boot_id);
618 log_error("Failed to get monotonic timestamp: %s", strerror(-r));
622 r = sd_journal_get_cursor(j, &cursor);
624 log_error("Failed to get cursor: %s", strerror(-r));
628 if (mode == OUTPUT_JSON_PRETTY)
631 "\t\"__CURSOR\" : \"%s\",\n"
632 "\t\"__REALTIME_TIMESTAMP\" : \"%llu\",\n"
633 "\t\"__MONOTONIC_TIMESTAMP\" : \"%llu\",\n"
634 "\t\"_BOOT_ID\" : \"%s\"",
636 (unsigned long long) realtime,
637 (unsigned long long) monotonic,
638 sd_id128_to_string(boot_id, sid));
640 if (mode == OUTPUT_JSON_SSE)
644 "{ \"__CURSOR\" : \"%s\", "
645 "\"__REALTIME_TIMESTAMP\" : \"%llu\", "
646 "\"__MONOTONIC_TIMESTAMP\" : \"%llu\", "
647 "\"_BOOT_ID\" : \"%s\"",
649 (unsigned long long) realtime,
650 (unsigned long long) monotonic,
651 sd_id128_to_string(boot_id, sid));
654 h = hashmap_new(string_hash_func, string_compare_func);
658 /* First round, iterate through the entry and count how often each field appears */
659 JOURNAL_FOREACH_DATA_RETVAL(j, data, length, r) {
665 memcmp(data, "_BOOT_ID=", 9) == 0)
668 eq = memchr(data, '=', length);
672 n = strndup(data, eq - (const char*) data);
678 u = PTR_TO_UINT(hashmap_get(h, n));
680 r = hashmap_put(h, n, UINT_TO_PTR(1));
686 r = hashmap_update(h, n, UINT_TO_PTR(u + 1));
700 SD_JOURNAL_FOREACH_DATA(j, data, length) {
706 /* We already printed the boot id, from the data in
707 * the header, hence let's suppress it here */
709 memcmp(data, "_BOOT_ID=", 9) == 0)
712 eq = memchr(data, '=', length);
717 if (mode == OUTPUT_JSON_PRETTY)
723 m = eq - (const char*) data;
725 n = strndup(data, m);
731 u = PTR_TO_UINT(hashmap_get2(h, n, (void**) &kk));
733 /* We already printed this, let's jump to the next */
739 /* Field only appears once, output it directly */
741 json_escape(f, data, m, flags);
744 json_escape(f, eq + 1, length - m - 1, flags);
746 hashmap_remove(h, n);
755 /* Field appears multiple times, output it as array */
756 json_escape(f, data, m, flags);
758 json_escape(f, eq + 1, length - m - 1, flags);
760 /* Iterate through the end of the list */
762 while (sd_journal_enumerate_data(j, &data, &length) > 0) {
766 if (memcmp(data, n, m) != 0)
769 if (((const char*) data)[m] != '=')
773 json_escape(f, (const char*) data + m + 1, length - m - 1, flags);
778 hashmap_remove(h, n);
782 /* Iterate data fields form the beginning */
792 if (mode == OUTPUT_JSON_PRETTY)
794 else if (mode == OUTPUT_JSON_SSE)
802 while ((k = hashmap_steal_first_key(h)))
810 static int output_cat(
824 sd_journal_set_data_threshold(j, 0);
826 r = sd_journal_get_data(j, "MESSAGE", &data, &l);
828 /* An entry without MESSAGE=? */
832 log_error("Failed to get data: %s", strerror(-r));
838 fwrite((const char*) data + 8, 1, l - 8, f);
844 static int (*output_funcs[_OUTPUT_MODE_MAX])(
849 OutputFlags flags) = {
851 [OUTPUT_SHORT] = output_short,
852 [OUTPUT_SHORT_MONOTONIC] = output_short,
853 [OUTPUT_SHORT_ISO] = output_short,
854 [OUTPUT_VERBOSE] = output_verbose,
855 [OUTPUT_EXPORT] = output_export,
856 [OUTPUT_JSON] = output_json,
857 [OUTPUT_JSON_PRETTY] = output_json,
858 [OUTPUT_JSON_SSE] = output_json,
859 [OUTPUT_CAT] = output_cat
872 assert(mode < _OUTPUT_MODE_MAX);
875 n_columns = columns();
877 ret = output_funcs[mode](f, j, mode, n_columns, flags);
880 if (ellipsized && ret > 0)
886 static int show_journal(FILE *f,
897 bool need_seek = false;
898 int warn_cutoff = flags & OUTPUT_WARN_CUTOFF;
902 assert(mode < _OUTPUT_MODE_MAX);
905 r = sd_journal_seek_tail(j);
909 r = sd_journal_previous_skip(j, how_many);
918 r = sd_journal_next(j);
928 if (not_before > 0) {
929 r = sd_journal_get_monotonic_usec(j, &usec, NULL);
931 /* -ESTALE is returned if the
932 timestamp is not from this boot */
938 if (usec < not_before)
944 r = output_journal(f, j, mode, n_columns, flags, ellipsized);
949 if (warn_cutoff && line < how_many && not_before > 0) {
953 /* Check whether the cutoff line is too early */
955 r = sd_id128_get_boot(&boot_id);
959 r = sd_journal_get_cutoff_monotonic_usec(j, boot_id, &cutoff, NULL);
963 if (r > 0 && not_before < cutoff)
964 fprintf(f, "Warning: Journal has been rotated since unit was started. Log output is incomplete or unavailable.\n");
969 if (!(flags & OUTPUT_FOLLOW))
972 r = sd_journal_wait(j, (usec_t) -1);
982 int add_matches_for_unit(sd_journal *j, const char *unit) {
984 char *m1, *m2, *m3, *m4;
989 m1 = strappenda("_SYSTEMD_UNIT=", unit);
990 m2 = strappenda("COREDUMP_UNIT=", unit);
991 m3 = strappenda("UNIT=", unit);
992 m4 = strappenda("OBJECT_SYSTEMD_UNIT=", unit);
995 /* Look for messages from the service itself */
996 (r = sd_journal_add_match(j, m1, 0)) ||
998 /* Look for coredumps of the service */
999 (r = sd_journal_add_disjunction(j)) ||
1000 (r = sd_journal_add_match(j, "MESSAGE_ID=fc2e22bc6ee647b6b90729ab34a250b1", 0)) ||
1001 (r = sd_journal_add_match(j, "_UID=0", 0)) ||
1002 (r = sd_journal_add_match(j, m2, 0)) ||
1004 /* Look for messages from PID 1 about this service */
1005 (r = sd_journal_add_disjunction(j)) ||
1006 (r = sd_journal_add_match(j, "_PID=1", 0)) ||
1007 (r = sd_journal_add_match(j, m3, 0)) ||
1009 /* Look for messages from authorized daemons about this service */
1010 (r = sd_journal_add_disjunction(j)) ||
1011 (r = sd_journal_add_match(j, "_UID=0", 0)) ||
1012 (r = sd_journal_add_match(j, m4, 0))
1018 int add_matches_for_user_unit(sd_journal *j, const char *unit, uid_t uid) {
1020 char *m1, *m2, *m3, *m4;
1021 char muid[sizeof("_UID=") + DECIMAL_STR_MAX(uid_t)];
1026 m1 = strappenda("_SYSTEMD_USER_UNIT=", unit);
1027 m2 = strappenda("USER_UNIT=", unit);
1028 m3 = strappenda("COREDUMP_USER_UNIT=", unit);
1029 m4 = strappenda("OBJECT_SYSTEMD_USER_UNIT=", unit);
1030 sprintf(muid, "_UID=%lu", (unsigned long) uid);
1033 /* Look for messages from the user service itself */
1034 (r = sd_journal_add_match(j, m1, 0)) ||
1035 (r = sd_journal_add_match(j, muid, 0)) ||
1037 /* Look for messages from systemd about this service */
1038 (r = sd_journal_add_disjunction(j)) ||
1039 (r = sd_journal_add_match(j, m2, 0)) ||
1040 (r = sd_journal_add_match(j, muid, 0)) ||
1042 /* Look for coredumps of the service */
1043 (r = sd_journal_add_disjunction(j)) ||
1044 (r = sd_journal_add_match(j, m3, 0)) ||
1045 (r = sd_journal_add_match(j, muid, 0)) ||
1046 (r = sd_journal_add_match(j, "_UID=0", 0)) ||
1048 /* Look for messages from authorized daemons about this service */
1049 (r = sd_journal_add_disjunction(j)) ||
1050 (r = sd_journal_add_match(j, m4, 0)) ||
1051 (r = sd_journal_add_match(j, muid, 0)) ||
1052 (r = sd_journal_add_match(j, "_UID=0", 0))
1057 int add_match_this_boot(sd_journal *j) {
1058 char match[9+32+1] = "_BOOT_ID=";
1064 r = sd_id128_get_boot(&boot_id);
1066 log_error("Failed to get boot id: %s", strerror(-r));
1070 sd_id128_to_string(boot_id, match + 9);
1071 r = sd_journal_add_match(j, match, strlen(match));
1073 log_error("Failed to add match: %s", strerror(-r));
1077 r = sd_journal_add_conjunction(j);
1084 int show_journal_by_unit(
1096 _cleanup_journal_close_ sd_journal*j = NULL;
1098 int jflags = SD_JOURNAL_LOCAL_ONLY | system * SD_JOURNAL_SYSTEM;
1101 assert(mode < _OUTPUT_MODE_MAX);
1107 r = sd_journal_open(&j, jflags);
1111 r = add_match_this_boot(j);
1116 r = add_matches_for_unit(j, unit);
1118 r = add_matches_for_user_unit(j, unit, uid);
1122 if (_unlikely_(log_get_max_level() >= LOG_PRI(LOG_DEBUG))) {
1123 _cleanup_free_ char *filter;
1125 filter = journal_make_match_string(j);
1126 log_debug("Journal filter: %s", filter);
1129 return show_journal(f, j, mode, n_columns, not_before, how_many, flags, ellipsized);
1132 static const char *const output_mode_table[_OUTPUT_MODE_MAX] = {
1133 [OUTPUT_SHORT] = "short",
1134 [OUTPUT_SHORT_MONOTONIC] = "short-monotonic",
1135 [OUTPUT_SHORT_ISO] = "short-iso",
1136 [OUTPUT_VERBOSE] = "verbose",
1137 [OUTPUT_EXPORT] = "export",
1138 [OUTPUT_JSON] = "json",
1139 [OUTPUT_JSON_PRETTY] = "json-pretty",
1140 [OUTPUT_JSON_SSE] = "json-sse",
1141 [OUTPUT_CAT] = "cat"
1144 DEFINE_STRING_TABLE_LOOKUP(output_mode, OutputMode);