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 #define PRINT_THRESHOLD 128
36 #define JSON_THRESHOLD 4096
38 static int print_catalog(FILE *f, sd_journal *j) {
40 _cleanup_free_ char *t = NULL, *z = NULL;
43 r = sd_journal_get_catalog(j, &t);
47 z = strreplace(strstrip(t), "\n", "\n-- ");
58 static int parse_field(const void *data, size_t length, const char *field, char **target, size_t *target_size) {
71 if (memcmp(data, field, fl))
79 memcpy(buf, (const char*) data + fl, nl);
89 static bool shall_print(const char *p, size_t l, OutputFlags flags) {
92 if (flags & OUTPUT_SHOW_ALL)
95 if (l >= PRINT_THRESHOLD)
98 if (!utf8_is_printable_n(p, l))
104 static int output_short(
115 _cleanup_free_ char *hostname = NULL, *identifier = NULL, *comm = NULL, *pid = NULL, *fake_pid = NULL, *message = NULL, *realtime = NULL, *monotonic = NULL, *priority = NULL;
116 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;
118 const char *color_on = "", *color_off = "";
123 sd_journal_set_data_threshold(j, flags & OUTPUT_SHOW_ALL ? 0 : PRINT_THRESHOLD);
125 SD_JOURNAL_FOREACH_DATA(j, data, length) {
127 r = parse_field(data, length, "PRIORITY=", &priority, &priority_len);
133 r = parse_field(data, length, "_HOSTNAME=", &hostname, &hostname_len);
139 r = parse_field(data, length, "SYSLOG_IDENTIFIER=", &identifier, &identifier_len);
145 r = parse_field(data, length, "_COMM=", &comm, &comm_len);
151 r = parse_field(data, length, "_PID=", &pid, &pid_len);
157 r = parse_field(data, length, "SYSLOG_PID=", &fake_pid, &fake_pid_len);
163 r = parse_field(data, length, "_SOURCE_REALTIME_TIMESTAMP=", &realtime, &realtime_len);
169 r = parse_field(data, length, "_SOURCE_MONOTONIC_TIMESTAMP=", &monotonic, &monotonic_len);
175 r = parse_field(data, length, "MESSAGE=", &message, &message_len);
183 if (!(flags & OUTPUT_SHOW_ALL))
184 strip_tab_ansi(&message, &message_len);
186 if (priority_len == 1 && *priority >= '0' && *priority <= '7')
189 if (mode == OUTPUT_SHORT_MONOTONIC) {
196 r = safe_atou64(monotonic, &t);
199 r = sd_journal_get_monotonic_usec(j, &t, &boot_id);
202 log_error("Failed to get monotonic: %s", strerror(-r));
206 fprintf(f, "[%5llu.%06llu]",
207 (unsigned long long) (t / USEC_PER_SEC),
208 (unsigned long long) (t % USEC_PER_SEC));
210 n += 1 + 5 + 1 + 6 + 1;
221 r = safe_atou64(realtime, &x);
224 r = sd_journal_get_realtime_usec(j, &x);
227 log_error("Failed to get realtime: %s", strerror(-r));
231 t = (time_t) (x / USEC_PER_SEC);
232 if (strftime(buf, sizeof(buf), "%b %d %H:%M:%S", localtime_r(&t, &tm)) <= 0) {
233 log_error("Failed to format time.");
241 if (hostname && shall_print(hostname, hostname_len, flags)) {
242 fprintf(f, " %.*s", (int) hostname_len, hostname);
243 n += hostname_len + 1;
246 if (identifier && shall_print(identifier, identifier_len, flags)) {
247 fprintf(f, " %.*s", (int) identifier_len, identifier);
248 n += identifier_len + 1;
249 } else if (comm && shall_print(comm, comm_len, flags)) {
250 fprintf(f, " %.*s", (int) comm_len, comm);
255 if (pid && shall_print(pid, pid_len, flags)) {
256 fprintf(f, "[%.*s]", (int) pid_len, pid);
258 } else if (fake_pid && shall_print(fake_pid, fake_pid_len, flags)) {
259 fprintf(f, "[%.*s]", (int) fake_pid_len, fake_pid);
260 n += fake_pid_len + 2;
263 if (flags & OUTPUT_COLOR) {
265 color_on = ANSI_HIGHLIGHT_RED_ON;
266 color_off = ANSI_HIGHLIGHT_OFF;
267 } else if (p <= LOG_NOTICE) {
268 color_on = ANSI_HIGHLIGHT_ON;
269 color_off = ANSI_HIGHLIGHT_OFF;
273 if (flags & OUTPUT_SHOW_ALL)
274 fprintf(f, ": %s%.*s%s\n", color_on, (int) message_len, message, color_off);
275 else if (!utf8_is_printable_n(message, message_len)) {
276 char bytes[FORMAT_BYTES_MAX];
277 fprintf(f, ": [%s blob data]\n", format_bytes(bytes, sizeof(bytes), message_len));
278 } else if ((flags & OUTPUT_FULL_WIDTH) || (message_len + n + 1 < n_columns))
279 fprintf(f, ": %s%.*s%s\n", color_on, (int) message_len, message, color_off);
280 else if (n < n_columns && n_columns - n - 2 >= 3) {
281 _cleanup_free_ char *e;
283 e = ellipsize_mem(message, message_len, n_columns - n - 2, 90);
286 fprintf(f, ": %s%.*s%s\n", color_on, (int) message_len, message, color_off);
288 fprintf(f, ": %s%s%s\n", color_on, e, color_off);
292 if (flags & OUTPUT_CATALOG)
298 static int output_verbose(
307 _cleanup_free_ char *cursor = NULL;
309 char ts[FORMAT_TIMESTAMP_MAX];
315 sd_journal_set_data_threshold(j, 0);
317 r = sd_journal_get_realtime_usec(j, &realtime);
319 log_error("Failed to get realtime timestamp: %s", strerror(-r));
323 r = sd_journal_get_cursor(j, &cursor);
325 log_error("Failed to get cursor: %s", strerror(-r));
329 fprintf(f, "%s [%s]\n",
330 format_timestamp(ts, sizeof(ts), realtime),
333 SD_JOURNAL_FOREACH_DATA(j, data, length) {
334 if (!shall_print(data, length, flags)) {
336 char bytes[FORMAT_BYTES_MAX];
338 c = memchr(data, '=', length);
340 log_error("Invalid field.");
344 fprintf(f, "\t%.*s=[%s blob data]\n",
345 (int) (c - (const char*) data),
347 format_bytes(bytes, sizeof(bytes), length - (c - (const char *) data) - 1));
349 fprintf(f, "\t%.*s\n", (int) length, (const char*) data);
352 if (flags & OUTPUT_CATALOG)
358 static int output_export(
368 usec_t realtime, monotonic;
369 _cleanup_free_ char *cursor = NULL;
375 sd_journal_set_data_threshold(j, 0);
377 r = sd_journal_get_realtime_usec(j, &realtime);
379 log_error("Failed to get realtime timestamp: %s", strerror(-r));
383 r = sd_journal_get_monotonic_usec(j, &monotonic, &boot_id);
385 log_error("Failed to get monotonic timestamp: %s", strerror(-r));
389 r = sd_journal_get_cursor(j, &cursor);
391 log_error("Failed to get cursor: %s", strerror(-r));
397 "__REALTIME_TIMESTAMP=%llu\n"
398 "__MONOTONIC_TIMESTAMP=%llu\n"
401 (unsigned long long) realtime,
402 (unsigned long long) monotonic,
403 sd_id128_to_string(boot_id, sid));
405 SD_JOURNAL_FOREACH_DATA(j, data, length) {
407 /* We already printed the boot id, from the data in
408 * the header, hence let's suppress it here */
410 memcmp(data, "_BOOT_ID=", 9) == 0)
413 if (!utf8_is_printable_n(data, length)) {
417 c = memchr(data, '=', length);
419 log_error("Invalid field.");
423 fwrite(data, c - (const char*) data, 1, f);
425 le64 = htole64(length - (c - (const char*) data) - 1);
426 fwrite(&le64, sizeof(le64), 1, f);
427 fwrite(c + 1, length - (c - (const char*) data) - 1, 1, f);
429 fwrite(data, length, 1, f);
448 if (!(flags & OUTPUT_SHOW_ALL) && l >= JSON_THRESHOLD)
452 else if (!utf8_is_printable_n(p, l)) {
453 bool not_first = false;
459 fprintf(f, ", %u", (uint8_t) *p);
462 fprintf(f, "%u", (uint8_t) *p);
474 if (*p == '"' || *p == '\\') {
478 fprintf(f, "\\u%04x", *p);
490 static int output_json(
497 uint64_t realtime, monotonic;
498 _cleanup_free_ char *cursor = NULL;
505 bool done, separator;
509 sd_journal_set_data_threshold(j, flags & OUTPUT_SHOW_ALL ? 0 : JSON_THRESHOLD);
511 r = sd_journal_get_realtime_usec(j, &realtime);
513 log_error("Failed to get realtime timestamp: %s", strerror(-r));
517 r = sd_journal_get_monotonic_usec(j, &monotonic, &boot_id);
519 log_error("Failed to get monotonic timestamp: %s", strerror(-r));
523 r = sd_journal_get_cursor(j, &cursor);
525 log_error("Failed to get cursor: %s", strerror(-r));
529 if (mode == OUTPUT_JSON_PRETTY)
532 "\t\"__CURSOR\" : \"%s\",\n"
533 "\t\"__REALTIME_TIMESTAMP\" : \"%llu\",\n"
534 "\t\"__MONOTONIC_TIMESTAMP\" : \"%llu\",\n"
535 "\t\"_BOOT_ID\" : \"%s\"",
537 (unsigned long long) realtime,
538 (unsigned long long) monotonic,
539 sd_id128_to_string(boot_id, sid));
541 if (mode == OUTPUT_JSON_SSE)
545 "{ \"__CURSOR\" : \"%s\", "
546 "\"__REALTIME_TIMESTAMP\" : \"%llu\", "
547 "\"__MONOTONIC_TIMESTAMP\" : \"%llu\", "
548 "\"_BOOT_ID\" : \"%s\"",
550 (unsigned long long) realtime,
551 (unsigned long long) monotonic,
552 sd_id128_to_string(boot_id, sid));
555 h = hashmap_new(string_hash_func, string_compare_func);
559 /* First round, iterate through the entry and count how often each field appears */
560 SD_JOURNAL_FOREACH_DATA(j, data, length) {
566 memcmp(data, "_BOOT_ID=", 9) == 0)
569 eq = memchr(data, '=', length);
573 n = strndup(data, eq - (const char*) data);
579 u = PTR_TO_UINT(hashmap_get(h, n));
581 r = hashmap_put(h, n, UINT_TO_PTR(1));
587 r = hashmap_update(h, n, UINT_TO_PTR(u + 1));
598 SD_JOURNAL_FOREACH_DATA(j, data, length) {
604 /* We already printed the boot id, from the data in
605 * the header, hence let's suppress it here */
607 memcmp(data, "_BOOT_ID=", 9) == 0)
610 eq = memchr(data, '=', length);
615 if (mode == OUTPUT_JSON_PRETTY)
621 m = eq - (const char*) data;
623 n = strndup(data, m);
629 u = PTR_TO_UINT(hashmap_get2(h, n, (void**) &kk));
631 /* We already printed this, let's jump to the next */
637 /* Field only appears once, output it directly */
639 json_escape(f, data, m, flags);
642 json_escape(f, eq + 1, length - m - 1, flags);
644 hashmap_remove(h, n);
653 /* Field appears multiple times, output it as array */
654 json_escape(f, data, m, flags);
656 json_escape(f, eq + 1, length - m - 1, flags);
658 /* Iterate through the end of the list */
660 while (sd_journal_enumerate_data(j, &data, &length) > 0) {
664 if (memcmp(data, n, m) != 0)
667 if (((const char*) data)[m] != '=')
671 json_escape(f, (const char*) data + m + 1, length - m - 1, flags);
676 hashmap_remove(h, n);
680 /* Iterate data fields form the beginning */
690 if (mode == OUTPUT_JSON_PRETTY)
692 else if (mode == OUTPUT_JSON_SSE)
700 while ((k = hashmap_steal_first_key(h)))
708 static int output_cat(
722 sd_journal_set_data_threshold(j, 0);
724 r = sd_journal_get_data(j, "MESSAGE", &data, &l);
726 /* An entry without MESSAGE=? */
730 log_error("Failed to get data: %s", strerror(-r));
736 fwrite((const char*) data + 8, 1, l - 8, f);
742 static int (*output_funcs[_OUTPUT_MODE_MAX])(
747 OutputFlags flags) = {
749 [OUTPUT_SHORT] = output_short,
750 [OUTPUT_SHORT_MONOTONIC] = output_short,
751 [OUTPUT_VERBOSE] = output_verbose,
752 [OUTPUT_EXPORT] = output_export,
753 [OUTPUT_JSON] = output_json,
754 [OUTPUT_JSON_PRETTY] = output_json,
755 [OUTPUT_JSON_SSE] = output_json,
756 [OUTPUT_CAT] = output_cat
768 assert(mode < _OUTPUT_MODE_MAX);
771 n_columns = columns();
773 ret = output_funcs[mode](f, j, mode, n_columns, flags);
778 static int show_journal(FILE *f,
788 bool need_seek = false;
789 int warn_cutoff = flags & OUTPUT_WARN_CUTOFF;
793 assert(mode < _OUTPUT_MODE_MAX);
796 r = sd_journal_seek_tail(j);
800 r = sd_journal_previous_skip(j, how_many);
809 r = sd_journal_next(j);
819 if (not_before > 0) {
820 r = sd_journal_get_monotonic_usec(j, &usec, NULL);
822 /* -ESTALE is returned if the
823 timestamp is not from this boot */
829 if (usec < not_before)
835 r = output_journal(f, j, mode, n_columns, flags);
840 if (warn_cutoff && line < how_many && not_before > 0) {
844 /* Check whether the cutoff line is too early */
846 r = sd_id128_get_boot(&boot_id);
850 r = sd_journal_get_cutoff_monotonic_usec(j, boot_id, &cutoff, NULL);
854 if (r > 0 && not_before < cutoff)
855 fprintf(f, "Warning: Journal has been rotated since unit was started. Log output is incomplete or unavailable.\n");
860 if (!(flags & OUTPUT_FOLLOW))
863 r = sd_journal_wait(j, (usec_t) -1);
873 int add_matches_for_unit(sd_journal *j, const char *unit) {
875 _cleanup_free_ char *m1 = NULL, *m2 = NULL, *m3 = NULL;
880 if (asprintf(&m1, "_SYSTEMD_UNIT=%s", unit) < 0 ||
881 asprintf(&m2, "COREDUMP_UNIT=%s", unit) < 0 ||
882 asprintf(&m3, "UNIT=%s", unit) < 0)
886 /* Look for messages from the service itself */
887 (r = sd_journal_add_match(j, m1, 0)) ||
889 /* Look for coredumps of the service */
890 (r = sd_journal_add_disjunction(j)) ||
891 (r = sd_journal_add_match(j,
892 "MESSAGE_ID=fc2e22bc6ee647b6b90729ab34a250b1", 0)) ||
893 (r = sd_journal_add_match(j, m2, 0)) ||
895 /* Look for messages from PID 1 about this service */
896 (r = sd_journal_add_disjunction(j)) ||
897 (r = sd_journal_add_match(j, "_PID=1", 0)) ||
898 (r = sd_journal_add_match(j, m3, 0))
903 int add_matches_for_user_unit(sd_journal *j, const char *unit, uid_t uid) {
905 _cleanup_free_ char *m1 = NULL, *m2 = NULL, *m3 = NULL, *m4 = NULL;
910 if (asprintf(&m1, "_SYSTEMD_USER_UNIT=%s", unit) < 0 ||
911 asprintf(&m2, "USER_UNIT=%s", unit) < 0 ||
912 asprintf(&m3, "COREDUMP_USER_UNIT=%s", unit) < 0 ||
913 asprintf(&m4, "_UID=%d", uid) < 0)
917 /* Look for messages from the user service itself */
918 (r = sd_journal_add_match(j, m1, 0)) ||
919 (r = sd_journal_add_match(j, m4, 0)) ||
921 /* Look for messages from systemd about this service */
922 (r = sd_journal_add_disjunction(j)) ||
923 (r = sd_journal_add_match(j, m2, 0)) ||
924 (r = sd_journal_add_match(j, m4, 0)) ||
926 /* Look for coredumps of the service */
927 (r = sd_journal_add_disjunction(j)) ||
928 (r = sd_journal_add_match(j, m3, 0)) ||
929 (r = sd_journal_add_match(j, m4, 0))
934 int show_journal_by_unit(
945 _cleanup_journal_close_ sd_journal*j = NULL;
947 int jflags = SD_JOURNAL_LOCAL_ONLY | system * SD_JOURNAL_SYSTEM_ONLY;
950 assert(mode < _OUTPUT_MODE_MAX);
956 r = sd_journal_open(&j, jflags);
961 r = add_matches_for_unit(j, unit);
963 r = add_matches_for_user_unit(j, unit, uid);
967 r = show_journal(f, j, mode, n_columns, not_before, how_many, flags);
974 static const char *const output_mode_table[_OUTPUT_MODE_MAX] = {
975 [OUTPUT_SHORT] = "short",
976 [OUTPUT_SHORT_MONOTONIC] = "short-monotonic",
977 [OUTPUT_VERBOSE] = "verbose",
978 [OUTPUT_EXPORT] = "export",
979 [OUTPUT_JSON] = "json",
980 [OUTPUT_JSON_PRETTY] = "json-pretty",
981 [OUTPUT_JSON_SSE] = "json-sse",
985 DEFINE_STRING_TABLE_LOOKUP(output_mode, OutputMode);