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"
34 #define PRINT_THRESHOLD 128
35 #define JSON_THRESHOLD 4096
37 static int print_catalog(FILE *f, sd_journal *j) {
39 _cleanup_free_ char *t = NULL, *z = NULL;
42 r = sd_journal_get_catalog(j, &t);
46 z = strreplace(strstrip(t), "\n", "\n-- ");
57 static int parse_field(const void *data, size_t length, const char *field, char **target, size_t *target_size) {
70 if (memcmp(data, field, fl))
78 memcpy(buf, (const char*) data + fl, nl);
88 static bool shall_print(const char *p, size_t l, OutputFlags flags) {
91 if (flags & OUTPUT_SHOW_ALL)
94 if (l >= PRINT_THRESHOLD)
97 if (!utf8_is_printable_n(p, l))
103 static int output_short(
114 _cleanup_free_ char *hostname = NULL, *identifier = NULL, *comm = NULL, *pid = NULL, *fake_pid = NULL, *message = NULL, *realtime = NULL, *monotonic = NULL, *priority = NULL;
115 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;
117 const char *color_on = "", *color_off = "";
122 sd_journal_set_data_threshold(j, flags & OUTPUT_SHOW_ALL ? 0 : PRINT_THRESHOLD);
124 SD_JOURNAL_FOREACH_DATA(j, data, length) {
126 r = parse_field(data, length, "PRIORITY=", &priority, &priority_len);
132 r = parse_field(data, length, "_HOSTNAME=", &hostname, &hostname_len);
138 r = parse_field(data, length, "SYSLOG_IDENTIFIER=", &identifier, &identifier_len);
144 r = parse_field(data, length, "_COMM=", &comm, &comm_len);
150 r = parse_field(data, length, "_PID=", &pid, &pid_len);
156 r = parse_field(data, length, "SYSLOG_PID=", &fake_pid, &fake_pid_len);
162 r = parse_field(data, length, "_SOURCE_REALTIME_TIMESTAMP=", &realtime, &realtime_len);
168 r = parse_field(data, length, "_SOURCE_MONOTONIC_TIMESTAMP=", &monotonic, &monotonic_len);
174 r = parse_field(data, length, "MESSAGE=", &message, &message_len);
182 if (!(flags & OUTPUT_SHOW_ALL))
183 strip_tab_ansi(&message, &message_len);
185 if (priority_len == 1 && *priority >= '0' && *priority <= '7')
188 if (mode == OUTPUT_SHORT_MONOTONIC) {
195 r = safe_atou64(monotonic, &t);
198 r = sd_journal_get_monotonic_usec(j, &t, &boot_id);
201 log_error("Failed to get monotonic: %s", strerror(-r));
205 fprintf(f, "[%5llu.%06llu]",
206 (unsigned long long) (t / USEC_PER_SEC),
207 (unsigned long long) (t % USEC_PER_SEC));
209 n += 1 + 5 + 1 + 6 + 1;
220 r = safe_atou64(realtime, &x);
223 r = sd_journal_get_realtime_usec(j, &x);
226 log_error("Failed to get realtime: %s", strerror(-r));
230 t = (time_t) (x / USEC_PER_SEC);
231 if (strftime(buf, sizeof(buf), "%b %d %H:%M:%S", localtime_r(&t, &tm)) <= 0) {
232 log_error("Failed to format time.");
240 if (hostname && shall_print(hostname, hostname_len, flags)) {
241 fprintf(f, " %.*s", (int) hostname_len, hostname);
242 n += hostname_len + 1;
245 if (identifier && shall_print(identifier, identifier_len, flags)) {
246 fprintf(f, " %.*s", (int) identifier_len, identifier);
247 n += identifier_len + 1;
248 } else if (comm && shall_print(comm, comm_len, flags)) {
249 fprintf(f, " %.*s", (int) comm_len, comm);
254 if (pid && shall_print(pid, pid_len, flags)) {
255 fprintf(f, "[%.*s]", (int) pid_len, pid);
257 } else if (fake_pid && shall_print(fake_pid, fake_pid_len, flags)) {
258 fprintf(f, "[%.*s]", (int) fake_pid_len, fake_pid);
259 n += fake_pid_len + 2;
262 if (flags & OUTPUT_COLOR) {
264 color_on = ANSI_HIGHLIGHT_RED_ON;
265 color_off = ANSI_HIGHLIGHT_OFF;
266 } else if (p <= LOG_NOTICE) {
267 color_on = ANSI_HIGHLIGHT_ON;
268 color_off = ANSI_HIGHLIGHT_OFF;
272 if (flags & OUTPUT_SHOW_ALL)
273 fprintf(f, ": %s%.*s%s\n", color_on, (int) message_len, message, color_off);
274 else if (!utf8_is_printable_n(message, message_len)) {
275 char bytes[FORMAT_BYTES_MAX];
276 fprintf(f, ": [%s blob data]\n", format_bytes(bytes, sizeof(bytes), message_len));
277 } else if ((flags & OUTPUT_FULL_WIDTH) || (message_len + n + 1 < n_columns))
278 fprintf(f, ": %s%.*s%s\n", color_on, (int) message_len, message, color_off);
279 else if (n < n_columns && n_columns - n - 2 >= 3) {
280 char _cleanup_free_ *e;
282 e = ellipsize_mem(message, message_len, n_columns - n - 2, 90);
285 fprintf(f, ": %s%.*s%s\n", color_on, (int) message_len, message, color_off);
287 fprintf(f, ": %s%s%s\n", color_on, e, color_off);
291 if (flags & OUTPUT_CATALOG)
297 static int output_verbose(
306 char _cleanup_free_ *cursor = NULL;
308 char ts[FORMAT_TIMESTAMP_MAX];
314 sd_journal_set_data_threshold(j, 0);
316 r = sd_journal_get_realtime_usec(j, &realtime);
318 log_error("Failed to get realtime timestamp: %s", strerror(-r));
322 r = sd_journal_get_cursor(j, &cursor);
324 log_error("Failed to get cursor: %s", strerror(-r));
328 fprintf(f, "%s [%s]\n",
329 format_timestamp(ts, sizeof(ts), realtime),
332 SD_JOURNAL_FOREACH_DATA(j, data, length) {
333 if (!shall_print(data, length, flags)) {
335 char bytes[FORMAT_BYTES_MAX];
337 c = memchr(data, '=', length);
339 log_error("Invalid field.");
343 fprintf(f, "\t%.*s=[%s blob data]\n",
344 (int) (c - (const char*) data),
346 format_bytes(bytes, sizeof(bytes), length - (c - (const char *) data) - 1));
348 fprintf(f, "\t%.*s\n", (int) length, (const char*) data);
351 if (flags & OUTPUT_CATALOG)
357 static int output_export(
367 usec_t realtime, monotonic;
368 char _cleanup_free_ *cursor = NULL;
374 sd_journal_set_data_threshold(j, 0);
376 r = sd_journal_get_realtime_usec(j, &realtime);
378 log_error("Failed to get realtime timestamp: %s", strerror(-r));
382 r = sd_journal_get_monotonic_usec(j, &monotonic, &boot_id);
384 log_error("Failed to get monotonic timestamp: %s", strerror(-r));
388 r = sd_journal_get_cursor(j, &cursor);
390 log_error("Failed to get cursor: %s", strerror(-r));
396 "__REALTIME_TIMESTAMP=%llu\n"
397 "__MONOTONIC_TIMESTAMP=%llu\n"
400 (unsigned long long) realtime,
401 (unsigned long long) monotonic,
402 sd_id128_to_string(boot_id, sid));
404 SD_JOURNAL_FOREACH_DATA(j, data, length) {
406 /* We already printed the boot id, from the data in
407 * the header, hence let's suppress it here */
409 memcmp(data, "_BOOT_ID=", 9) == 0)
412 if (!utf8_is_printable_n(data, length)) {
416 c = memchr(data, '=', length);
418 log_error("Invalid field.");
422 fwrite(data, c - (const char*) data, 1, f);
424 le64 = htole64(length - (c - (const char*) data) - 1);
425 fwrite(&le64, sizeof(le64), 1, f);
426 fwrite(c + 1, length - (c - (const char*) data) - 1, 1, f);
428 fwrite(data, length, 1, f);
447 if (!(flags & OUTPUT_SHOW_ALL) && l >= JSON_THRESHOLD)
451 else if (!utf8_is_printable_n(p, l)) {
452 bool not_first = false;
458 fprintf(f, ", %u", (uint8_t) *p);
461 fprintf(f, "%u", (uint8_t) *p);
473 if (*p == '"' || *p == '\\') {
477 fprintf(f, "\\u%04x", *p);
489 static int output_json(
496 uint64_t realtime, monotonic;
497 char _cleanup_free_ *cursor = NULL;
504 bool done, separator;
508 sd_journal_set_data_threshold(j, flags & OUTPUT_SHOW_ALL ? 0 : JSON_THRESHOLD);
510 r = sd_journal_get_realtime_usec(j, &realtime);
512 log_error("Failed to get realtime timestamp: %s", strerror(-r));
516 r = sd_journal_get_monotonic_usec(j, &monotonic, &boot_id);
518 log_error("Failed to get monotonic timestamp: %s", strerror(-r));
522 r = sd_journal_get_cursor(j, &cursor);
524 log_error("Failed to get cursor: %s", strerror(-r));
528 if (mode == OUTPUT_JSON_PRETTY)
531 "\t\"__CURSOR\" : \"%s\",\n"
532 "\t\"__REALTIME_TIMESTAMP\" : \"%llu\",\n"
533 "\t\"__MONOTONIC_TIMESTAMP\" : \"%llu\",\n"
534 "\t\"_BOOT_ID\" : \"%s\"",
536 (unsigned long long) realtime,
537 (unsigned long long) monotonic,
538 sd_id128_to_string(boot_id, sid));
540 if (mode == OUTPUT_JSON_SSE)
544 "{ \"__CURSOR\" : \"%s\", "
545 "\"__REALTIME_TIMESTAMP\" : \"%llu\", "
546 "\"__MONOTONIC_TIMESTAMP\" : \"%llu\", "
547 "\"_BOOT_ID\" : \"%s\"",
549 (unsigned long long) realtime,
550 (unsigned long long) monotonic,
551 sd_id128_to_string(boot_id, sid));
554 h = hashmap_new(string_hash_func, string_compare_func);
558 /* First round, iterate through the entry and count how often each field appears */
559 SD_JOURNAL_FOREACH_DATA(j, data, length) {
565 memcmp(data, "_BOOT_ID=", 9) == 0)
568 eq = memchr(data, '=', length);
572 n = strndup(data, eq - (const char*) data);
578 u = PTR_TO_UINT(hashmap_get(h, n));
580 r = hashmap_put(h, n, UINT_TO_PTR(1));
586 r = hashmap_update(h, n, UINT_TO_PTR(u + 1));
597 SD_JOURNAL_FOREACH_DATA(j, data, length) {
603 /* We already printed the boot id, from the data in
604 * the header, hence let's suppress it here */
606 memcmp(data, "_BOOT_ID=", 9) == 0)
609 eq = memchr(data, '=', length);
614 if (mode == OUTPUT_JSON_PRETTY)
620 m = eq - (const char*) data;
622 n = strndup(data, m);
628 u = PTR_TO_UINT(hashmap_get2(h, n, (void**) &kk));
630 /* We already printed this, let's jump to the next */
636 /* Field only appears once, output it directly */
638 json_escape(f, data, m, flags);
641 json_escape(f, eq + 1, length - m - 1, flags);
643 hashmap_remove(h, n);
652 /* Field appears multiple times, output it as array */
653 json_escape(f, data, m, flags);
655 json_escape(f, eq + 1, length - m - 1, flags);
657 /* Iterate through the end of the list */
659 while (sd_journal_enumerate_data(j, &data, &length) > 0) {
663 if (memcmp(data, n, m) != 0)
666 if (((const char*) data)[m] != '=')
670 json_escape(f, (const char*) data + m + 1, length - m - 1, flags);
675 hashmap_remove(h, n);
679 /* Iterate data fields form the beginning */
689 if (mode == OUTPUT_JSON_PRETTY)
691 else if (mode == OUTPUT_JSON_SSE)
699 while ((k = hashmap_steal_first_key(h)))
707 static int output_cat(
721 sd_journal_set_data_threshold(j, 0);
723 r = sd_journal_get_data(j, "MESSAGE", &data, &l);
725 /* An entry without MESSAGE=? */
729 log_error("Failed to get data: %s", strerror(-r));
735 fwrite((const char*) data + 8, 1, l - 8, f);
741 static int (*output_funcs[_OUTPUT_MODE_MAX])(
746 OutputFlags flags) = {
748 [OUTPUT_SHORT] = output_short,
749 [OUTPUT_SHORT_MONOTONIC] = output_short,
750 [OUTPUT_VERBOSE] = output_verbose,
751 [OUTPUT_EXPORT] = output_export,
752 [OUTPUT_JSON] = output_json,
753 [OUTPUT_JSON_PRETTY] = output_json,
754 [OUTPUT_JSON_SSE] = output_json,
755 [OUTPUT_CAT] = output_cat
767 assert(mode < _OUTPUT_MODE_MAX);
770 n_columns = columns();
772 ret = output_funcs[mode](f, j, mode, n_columns, flags);
777 static int show_journal(FILE *f,
787 bool need_seek = false;
788 int warn_cutoff = flags & OUTPUT_WARN_CUTOFF;
792 assert(mode < _OUTPUT_MODE_MAX);
795 r = sd_journal_seek_tail(j);
799 r = sd_journal_previous_skip(j, how_many);
808 r = sd_journal_next(j);
818 if (not_before > 0) {
819 r = sd_journal_get_monotonic_usec(j, &usec, NULL);
821 /* -ESTALE is returned if the
822 timestamp is not from this boot */
828 if (usec < not_before)
834 r = output_journal(f, j, mode, n_columns, flags);
839 if (warn_cutoff && line < how_many && not_before > 0) {
843 /* Check whether the cutoff line is too early */
845 r = sd_id128_get_boot(&boot_id);
849 r = sd_journal_get_cutoff_monotonic_usec(j, boot_id, &cutoff, NULL);
853 if (r > 0 && not_before < cutoff)
854 fprintf(f, "Warning: Journal has been rotated since unit was started. Log output is incomplete or unavailable.\n");
859 if (!(flags & OUTPUT_FOLLOW))
862 r = sd_journal_wait(j, (usec_t) -1);
872 int show_journal_by_unit(
881 _cleanup_free_ char *m1 = NULL, *m2 = NULL, *m3 = NULL;
882 sd_journal *j = NULL;
886 assert(mode < _OUTPUT_MODE_MAX);
892 if (asprintf(&m1, "_SYSTEMD_UNIT=%s", unit) < 0 ||
893 asprintf(&m2, "COREDUMP_UNIT=%s", unit) < 0 ||
894 asprintf(&m3, "UNIT=%s", unit) < 0) {
899 r = sd_journal_open(&j, SD_JOURNAL_LOCAL_ONLY|SD_JOURNAL_SYSTEM_ONLY);
903 /* Look for messages from the service itself */
904 r = sd_journal_add_match(j, m1, 0);
908 /* Look for coredumps of the service */
909 r = sd_journal_add_disjunction(j);
912 r = sd_journal_add_match(j, "MESSAGE_ID=fc2e22bc6ee647b6b90729ab34a250b1", 0);
915 r = sd_journal_add_match(j, m2, 0);
919 /* Look for messages from PID 1 about this service */
920 r = sd_journal_add_disjunction(j);
923 r = sd_journal_add_match(j, "_PID=1", 0);
926 r = sd_journal_add_match(j, m3, 0);
930 r = show_journal(f, j, mode, n_columns, not_before, how_many, flags);
941 int show_journal_by_user_unit(
951 _cleanup_free_ char *m1 = NULL, *m2 = NULL, *m3 = NULL, *m4 = NULL;
952 sd_journal *j = NULL;
956 assert(mode < _OUTPUT_MODE_MAX);
962 if (asprintf(&m1, "_SYSTEMD_USER_UNIT=%s", unit) < 0 ||
963 asprintf(&m2, "USER_UNIT=%s", unit) < 0 ||
964 asprintf(&m3, "COREDUMP_USER_UNIT=%s", unit) < 0 ||
965 asprintf(&m4, "_UID=%d", uid) < 0) {
970 r = sd_journal_open(&j, SD_JOURNAL_LOCAL_ONLY);
974 /* Look for messages from the user service itself */
975 r = sd_journal_add_match(j, m1, 0);
978 r = sd_journal_add_match(j, m4, 0);
982 /* Look for messages from systemd about this service */
983 r = sd_journal_add_disjunction(j);
986 r = sd_journal_add_match(j, m2, 0);
989 r = sd_journal_add_match(j, m4, 0);
993 /* Look for coredumps of the service */
994 r = sd_journal_add_disjunction(j);
997 r = sd_journal_add_match(j, m3, 0);
1000 r = sd_journal_add_match(j, m4, 0);
1004 r = show_journal(f, j, mode, n_columns, not_before, how_many, flags);
1010 sd_journal_close(j);
1015 static const char *const output_mode_table[_OUTPUT_MODE_MAX] = {
1016 [OUTPUT_SHORT] = "short",
1017 [OUTPUT_SHORT_MONOTONIC] = "short-monotonic",
1018 [OUTPUT_VERBOSE] = "verbose",
1019 [OUTPUT_EXPORT] = "export",
1020 [OUTPUT_JSON] = "json",
1021 [OUTPUT_JSON_PRETTY] = "json-pretty",
1022 [OUTPUT_JSON_SSE] = "json-sse",
1023 [OUTPUT_CAT] = "cat"
1026 DEFINE_STRING_TABLE_LOOKUP(output_mode, OutputMode);