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 not-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 + PRINT_CHAR_THRESHOLD;
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);
323 case OUTPUT_SHORT_ISO:
324 r = strftime(buf, sizeof(buf), "%Y-%m-%dT%H:%M:%S%z", localtime_r(&t, &tm));
326 case OUTPUT_SHORT_PRECISE:
327 r = strftime(buf, sizeof(buf), "%b %d %H:%M:%S", localtime_r(&t, &tm));
329 snprintf(buf + strlen(buf), sizeof(buf) - strlen(buf),
330 ".%06llu", x % USEC_PER_SEC);
334 r = strftime(buf, sizeof(buf), "%b %d %H:%M:%S", localtime_r(&t, &tm));
338 log_error("Failed to format time.");
346 if (hostname && shall_print(hostname, hostname_len, flags)) {
347 fprintf(f, " %.*s", (int) hostname_len, hostname);
348 n += hostname_len + 1;
351 if (identifier && shall_print(identifier, identifier_len, flags)) {
352 fprintf(f, " %.*s", (int) identifier_len, identifier);
353 n += identifier_len + 1;
354 } else if (comm && shall_print(comm, comm_len, flags)) {
355 fprintf(f, " %.*s", (int) comm_len, comm);
360 if (pid && shall_print(pid, pid_len, flags)) {
361 fprintf(f, "[%.*s]", (int) pid_len, pid);
363 } else if (fake_pid && shall_print(fake_pid, fake_pid_len, flags)) {
364 fprintf(f, "[%.*s]", (int) fake_pid_len, fake_pid);
365 n += fake_pid_len + 2;
368 if (!(flags & OUTPUT_SHOW_ALL) && !utf8_is_printable(message, message_len)) {
369 char bytes[FORMAT_BYTES_MAX];
370 fprintf(f, ": [%s blob data]\n", format_bytes(bytes, sizeof(bytes), message_len));
374 print_multiline(f, n + 2, n_columns, flags, p, message, message_len);
377 if (flags & OUTPUT_CATALOG)
383 static int output_verbose(
392 _cleanup_free_ char *cursor = NULL;
394 char ts[FORMAT_TIMESTAMP_MAX + 7];
400 sd_journal_set_data_threshold(j, 0);
402 r = sd_journal_get_data(j, "_SOURCE_REALTIME_TIMESTAMP", &data, &length);
404 log_debug("Source realtime timestamp not found");
406 log_full(r == -EADDRNOTAVAIL ? LOG_DEBUG : LOG_ERR,
407 "Failed to get source realtime timestamp: %s", strerror(-r));
410 _cleanup_free_ char *value = NULL;
413 r = parse_field(data, length, "_SOURCE_REALTIME_TIMESTAMP=", &value, &size);
415 log_debug("_SOURCE_REALTIME_TIMESTAMP invalid: %s", strerror(-r));
417 r = safe_atou64(value, &realtime);
419 log_debug("Failed to parse realtime timestamp: %s",
425 r = sd_journal_get_realtime_usec(j, &realtime);
427 log_full(r == -EADDRNOTAVAIL ? LOG_DEBUG : LOG_ERR,
428 "Failed to get realtime timestamp: %s", strerror(-r));
433 r = sd_journal_get_cursor(j, &cursor);
435 log_error("Failed to get cursor: %s", strerror(-r));
439 fprintf(f, "%s [%s]\n",
440 format_timestamp_us(ts, sizeof(ts), realtime),
443 JOURNAL_FOREACH_DATA_RETVAL(j, data, length, r) {
446 const char *on = "", *off = "";
448 c = memchr(data, '=', length);
450 log_error("Invalid field.");
453 fieldlen = c - (const char*) data;
455 if (flags & OUTPUT_COLOR && startswith(data, "MESSAGE=")) {
456 on = ANSI_HIGHLIGHT_ON;
457 off = ANSI_HIGHLIGHT_OFF;
460 if (flags & OUTPUT_SHOW_ALL ||
461 (((length < PRINT_CHAR_THRESHOLD) || flags & OUTPUT_FULL_WIDTH)
462 && utf8_is_printable(data, length))) {
463 fprintf(f, " %s%.*s=", on, fieldlen, (const char*)data);
464 print_multiline(f, 4 + fieldlen + 1, 0, OUTPUT_FULL_WIDTH, 0, c + 1, length - fieldlen - 1);
467 char bytes[FORMAT_BYTES_MAX];
469 fprintf(f, " %s%.*s=[%s blob data]%s\n",
471 (int) (c - (const char*) data),
473 format_bytes(bytes, sizeof(bytes), length - (c - (const char *) data) - 1),
481 if (flags & OUTPUT_CATALOG)
487 static int output_export(
497 usec_t realtime, monotonic;
498 _cleanup_free_ char *cursor = NULL;
504 sd_journal_set_data_threshold(j, 0);
506 r = sd_journal_get_realtime_usec(j, &realtime);
508 log_error("Failed to get realtime timestamp: %s", strerror(-r));
512 r = sd_journal_get_monotonic_usec(j, &monotonic, &boot_id);
514 log_error("Failed to get monotonic timestamp: %s", strerror(-r));
518 r = sd_journal_get_cursor(j, &cursor);
520 log_error("Failed to get cursor: %s", strerror(-r));
526 "__REALTIME_TIMESTAMP=%llu\n"
527 "__MONOTONIC_TIMESTAMP=%llu\n"
530 (unsigned long long) realtime,
531 (unsigned long long) monotonic,
532 sd_id128_to_string(boot_id, sid));
534 JOURNAL_FOREACH_DATA_RETVAL(j, data, length, r) {
536 /* We already printed the boot id, from the data in
537 * the header, hence let's suppress it here */
539 startswith(data, "_BOOT_ID="))
542 if (!utf8_is_printable(data, length)) {
546 c = memchr(data, '=', length);
548 log_error("Invalid field.");
552 fwrite(data, c - (const char*) data, 1, f);
554 le64 = htole64(length - (c - (const char*) data) - 1);
555 fwrite(&le64, sizeof(le64), 1, f);
556 fwrite(c + 1, length - (c - (const char*) data) - 1, 1, f);
558 fwrite(data, length, 1, f);
580 if (!(flags & OUTPUT_SHOW_ALL) && l >= JSON_THRESHOLD)
584 else if (!utf8_is_printable(p, l)) {
585 bool not_first = false;
591 fprintf(f, ", %u", (uint8_t) *p);
594 fprintf(f, "%u", (uint8_t) *p);
606 if (*p == '"' || *p == '\\') {
609 } else if (*p == '\n')
612 fprintf(f, "\\u%04x", *p);
624 static int output_json(
631 uint64_t realtime, monotonic;
632 _cleanup_free_ char *cursor = NULL;
639 bool done, separator;
643 sd_journal_set_data_threshold(j, flags & OUTPUT_SHOW_ALL ? 0 : JSON_THRESHOLD);
645 r = sd_journal_get_realtime_usec(j, &realtime);
647 log_error("Failed to get realtime timestamp: %s", strerror(-r));
651 r = sd_journal_get_monotonic_usec(j, &monotonic, &boot_id);
653 log_error("Failed to get monotonic timestamp: %s", strerror(-r));
657 r = sd_journal_get_cursor(j, &cursor);
659 log_error("Failed to get cursor: %s", strerror(-r));
663 if (mode == OUTPUT_JSON_PRETTY)
666 "\t\"__CURSOR\" : \"%s\",\n"
667 "\t\"__REALTIME_TIMESTAMP\" : \"%llu\",\n"
668 "\t\"__MONOTONIC_TIMESTAMP\" : \"%llu\",\n"
669 "\t\"_BOOT_ID\" : \"%s\"",
671 (unsigned long long) realtime,
672 (unsigned long long) monotonic,
673 sd_id128_to_string(boot_id, sid));
675 if (mode == OUTPUT_JSON_SSE)
679 "{ \"__CURSOR\" : \"%s\", "
680 "\"__REALTIME_TIMESTAMP\" : \"%llu\", "
681 "\"__MONOTONIC_TIMESTAMP\" : \"%llu\", "
682 "\"_BOOT_ID\" : \"%s\"",
684 (unsigned long long) realtime,
685 (unsigned long long) monotonic,
686 sd_id128_to_string(boot_id, sid));
689 h = hashmap_new(string_hash_func, string_compare_func);
693 /* First round, iterate through the entry and count how often each field appears */
694 JOURNAL_FOREACH_DATA_RETVAL(j, data, length, r) {
700 memcmp(data, "_BOOT_ID=", 9) == 0)
703 eq = memchr(data, '=', length);
707 n = strndup(data, eq - (const char*) data);
713 u = PTR_TO_UINT(hashmap_get(h, n));
715 r = hashmap_put(h, n, UINT_TO_PTR(1));
721 r = hashmap_update(h, n, UINT_TO_PTR(u + 1));
735 SD_JOURNAL_FOREACH_DATA(j, data, length) {
741 /* We already printed the boot id, from the data in
742 * the header, hence let's suppress it here */
744 memcmp(data, "_BOOT_ID=", 9) == 0)
747 eq = memchr(data, '=', length);
752 if (mode == OUTPUT_JSON_PRETTY)
758 m = eq - (const char*) data;
760 n = strndup(data, m);
766 u = PTR_TO_UINT(hashmap_get2(h, n, (void**) &kk));
768 /* We already printed this, let's jump to the next */
774 /* Field only appears once, output it directly */
776 json_escape(f, data, m, flags);
779 json_escape(f, eq + 1, length - m - 1, flags);
781 hashmap_remove(h, n);
790 /* Field appears multiple times, output it as array */
791 json_escape(f, data, m, flags);
793 json_escape(f, eq + 1, length - m - 1, flags);
795 /* Iterate through the end of the list */
797 while (sd_journal_enumerate_data(j, &data, &length) > 0) {
801 if (memcmp(data, n, m) != 0)
804 if (((const char*) data)[m] != '=')
808 json_escape(f, (const char*) data + m + 1, length - m - 1, flags);
813 hashmap_remove(h, n);
817 /* Iterate data fields form the beginning */
827 if (mode == OUTPUT_JSON_PRETTY)
829 else if (mode == OUTPUT_JSON_SSE)
837 while ((k = hashmap_steal_first_key(h)))
845 static int output_cat(
859 sd_journal_set_data_threshold(j, 0);
861 r = sd_journal_get_data(j, "MESSAGE", &data, &l);
863 /* An entry without MESSAGE=? */
867 log_error("Failed to get data: %s", strerror(-r));
873 fwrite((const char*) data + 8, 1, l - 8, f);
879 static int (*output_funcs[_OUTPUT_MODE_MAX])(
884 OutputFlags flags) = {
886 [OUTPUT_SHORT] = output_short,
887 [OUTPUT_SHORT_ISO] = output_short,
888 [OUTPUT_SHORT_PRECISE] = output_short,
889 [OUTPUT_SHORT_MONOTONIC] = output_short,
890 [OUTPUT_VERBOSE] = output_verbose,
891 [OUTPUT_EXPORT] = output_export,
892 [OUTPUT_JSON] = output_json,
893 [OUTPUT_JSON_PRETTY] = output_json,
894 [OUTPUT_JSON_SSE] = output_json,
895 [OUTPUT_CAT] = output_cat
908 assert(mode < _OUTPUT_MODE_MAX);
911 n_columns = columns();
913 ret = output_funcs[mode](f, j, mode, n_columns, flags);
916 if (ellipsized && ret > 0)
922 static int show_journal(FILE *f,
933 bool need_seek = false;
934 int warn_cutoff = flags & OUTPUT_WARN_CUTOFF;
938 assert(mode < _OUTPUT_MODE_MAX);
941 r = sd_journal_seek_tail(j);
945 r = sd_journal_previous_skip(j, how_many);
954 r = sd_journal_next(j);
964 if (not_before > 0) {
965 r = sd_journal_get_monotonic_usec(j, &usec, NULL);
967 /* -ESTALE is returned if the
968 timestamp is not from this boot */
974 if (usec < not_before)
980 r = output_journal(f, j, mode, n_columns, flags, ellipsized);
985 if (warn_cutoff && line < how_many && not_before > 0) {
989 /* Check whether the cutoff line is too early */
991 r = sd_id128_get_boot(&boot_id);
995 r = sd_journal_get_cutoff_monotonic_usec(j, boot_id, &cutoff, NULL);
999 if (r > 0 && not_before < cutoff)
1000 fprintf(f, "Warning: Journal has been rotated since unit was started. Log output is incomplete or unavailable.\n");
1002 warn_cutoff = false;
1005 if (!(flags & OUTPUT_FOLLOW))
1008 r = sd_journal_wait(j, (usec_t) -1);
1018 int add_matches_for_unit(sd_journal *j, const char *unit) {
1020 char *m1, *m2, *m3, *m4;
1025 m1 = strappenda("_SYSTEMD_UNIT=", unit);
1026 m2 = strappenda("COREDUMP_UNIT=", unit);
1027 m3 = strappenda("UNIT=", unit);
1028 m4 = strappenda("OBJECT_SYSTEMD_UNIT=", unit);
1031 /* Look for messages from the service itself */
1032 (r = sd_journal_add_match(j, m1, 0)) ||
1034 /* Look for coredumps of the service */
1035 (r = sd_journal_add_disjunction(j)) ||
1036 (r = sd_journal_add_match(j, "MESSAGE_ID=fc2e22bc6ee647b6b90729ab34a250b1", 0)) ||
1037 (r = sd_journal_add_match(j, "_UID=0", 0)) ||
1038 (r = sd_journal_add_match(j, m2, 0)) ||
1040 /* Look for messages from PID 1 about this service */
1041 (r = sd_journal_add_disjunction(j)) ||
1042 (r = sd_journal_add_match(j, "_PID=1", 0)) ||
1043 (r = sd_journal_add_match(j, m3, 0)) ||
1045 /* Look for messages from authorized daemons about this service */
1046 (r = sd_journal_add_disjunction(j)) ||
1047 (r = sd_journal_add_match(j, "_UID=0", 0)) ||
1048 (r = sd_journal_add_match(j, m4, 0))
1054 int add_matches_for_user_unit(sd_journal *j, const char *unit, uid_t uid) {
1056 char *m1, *m2, *m3, *m4;
1057 char muid[sizeof("_UID=") + DECIMAL_STR_MAX(uid_t)];
1062 m1 = strappenda("_SYSTEMD_USER_UNIT=", unit);
1063 m2 = strappenda("USER_UNIT=", unit);
1064 m3 = strappenda("COREDUMP_USER_UNIT=", unit);
1065 m4 = strappenda("OBJECT_SYSTEMD_USER_UNIT=", unit);
1066 sprintf(muid, "_UID=%lu", (unsigned long) uid);
1069 /* Look for messages from the user service itself */
1070 (r = sd_journal_add_match(j, m1, 0)) ||
1071 (r = sd_journal_add_match(j, muid, 0)) ||
1073 /* Look for messages from systemd about this service */
1074 (r = sd_journal_add_disjunction(j)) ||
1075 (r = sd_journal_add_match(j, m2, 0)) ||
1076 (r = sd_journal_add_match(j, muid, 0)) ||
1078 /* Look for coredumps of the service */
1079 (r = sd_journal_add_disjunction(j)) ||
1080 (r = sd_journal_add_match(j, m3, 0)) ||
1081 (r = sd_journal_add_match(j, muid, 0)) ||
1082 (r = sd_journal_add_match(j, "_UID=0", 0)) ||
1084 /* Look for messages from authorized daemons about this service */
1085 (r = sd_journal_add_disjunction(j)) ||
1086 (r = sd_journal_add_match(j, m4, 0)) ||
1087 (r = sd_journal_add_match(j, muid, 0)) ||
1088 (r = sd_journal_add_match(j, "_UID=0", 0))
1093 int add_match_this_boot(sd_journal *j) {
1094 char match[9+32+1] = "_BOOT_ID=";
1100 r = sd_id128_get_boot(&boot_id);
1102 log_error("Failed to get boot id: %s", strerror(-r));
1106 sd_id128_to_string(boot_id, match + 9);
1107 r = sd_journal_add_match(j, match, strlen(match));
1109 log_error("Failed to add match: %s", strerror(-r));
1113 r = sd_journal_add_conjunction(j);
1120 int show_journal_by_unit(
1132 _cleanup_journal_close_ sd_journal*j = NULL;
1134 int jflags = SD_JOURNAL_LOCAL_ONLY | system * SD_JOURNAL_SYSTEM;
1137 assert(mode < _OUTPUT_MODE_MAX);
1143 r = sd_journal_open(&j, jflags);
1147 r = add_match_this_boot(j);
1152 r = add_matches_for_unit(j, unit);
1154 r = add_matches_for_user_unit(j, unit, uid);
1158 if (_unlikely_(log_get_max_level() >= LOG_PRI(LOG_DEBUG))) {
1159 _cleanup_free_ char *filter;
1161 filter = journal_make_match_string(j);
1162 log_debug("Journal filter: %s", filter);
1165 return show_journal(f, j, mode, n_columns, not_before, how_many, flags, ellipsized);
1168 static const char *const output_mode_table[_OUTPUT_MODE_MAX] = {
1169 [OUTPUT_SHORT] = "short",
1170 [OUTPUT_SHORT_ISO] = "short-iso",
1171 [OUTPUT_SHORT_PRECISE] = "short-precise",
1172 [OUTPUT_SHORT_MONOTONIC] = "short-monotonic",
1173 [OUTPUT_VERBOSE] = "verbose",
1174 [OUTPUT_EXPORT] = "export",
1175 [OUTPUT_JSON] = "json",
1176 [OUTPUT_JSON_PRETTY] = "json-pretty",
1177 [OUTPUT_JSON_SSE] = "json-sse",
1178 [OUTPUT_CAT] = "cat"
1181 DEFINE_STRING_TABLE_LOOKUP(output_mode, OutputMode);