mirror of
https://github.com/systemd/systemd.git
synced 2025-03-31 14:50:15 +03:00
Merge pull request #24686 from d4nuu8/delta_output
shared/logs-show: add new --output= format "short-delta"
This commit is contained in:
commit
addc84ec91
4
NEWS
4
NEWS
@ -165,6 +165,10 @@ CHANGES WITH 252 in spe:
|
||||
* openssl is the default crypto backend for systemd-resolved. (gnutls
|
||||
is still supported.)
|
||||
|
||||
* journalctl -o (and similar commands) now understands a new output mode
|
||||
"short-delta". It is similar to "short-monotonic" but also shows the
|
||||
time delta between two messages.
|
||||
|
||||
Experimental features:
|
||||
|
||||
* BPF programs can now be compiled with bpf-gcc.
|
||||
|
@ -419,6 +419,13 @@
|
||||
timestamps.</para></listitem>
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry>
|
||||
<term><option>short-delta</option></term>
|
||||
<listitem><para>as for <option>short-monotonic</option> but includes the time difference
|
||||
to the previous entry.
|
||||
Maybe unreliable time differences are marked by a <literal>*</literal>.</para></listitem>
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry>
|
||||
<term><option>short-unix</option></term>
|
||||
<listitem><para>is very similar, but shows seconds passed since January 1st 1970 UTC instead of
|
||||
|
@ -2,5 +2,5 @@
|
||||
# SPDX-License-Identifier: LGPL-2.1-or-later
|
||||
|
||||
local -a _output_opts
|
||||
_output_opts=(short short-full short-iso short-iso-precise short-precise short-monotonic short-unix verbose export json json-pretty json-sse json-seq cat with-unit)
|
||||
_output_opts=(short short-full short-iso short-iso-precise short-precise short-monotonic short-unix short-delta verbose export json json-pretty json-sse json-seq cat with-unit)
|
||||
_describe -t output 'output mode' _output_opts || compadd "$@"
|
||||
|
@ -148,6 +148,8 @@ static ssize_t request_reader_entries(
|
||||
size_t max) {
|
||||
|
||||
RequestMeta *m = ASSERT_PTR(cls);
|
||||
dual_timestamp previous_ts = DUAL_TIMESTAMP_NULL;
|
||||
sd_id128_t previous_boot_id = SD_ID128_NULL;
|
||||
int r;
|
||||
size_t n, k;
|
||||
|
||||
@ -222,7 +224,7 @@ static ssize_t request_reader_entries(
|
||||
}
|
||||
|
||||
r = show_journal_entry(m->tmp, m->journal, m->mode, 0, OUTPUT_FULL_WIDTH,
|
||||
NULL, NULL, NULL);
|
||||
NULL, NULL, NULL, &previous_ts, &previous_boot_id);
|
||||
if (r < 0) {
|
||||
log_error_errno(r, "Failed to serialize item: %m");
|
||||
return MHD_CONTENT_READER_END_WITH_ERROR;
|
||||
|
@ -2099,7 +2099,8 @@ int main(int argc, char *argv[]) {
|
||||
bool previous_boot_id_valid = false, first_line = true, ellipsized = false, need_seek = false;
|
||||
bool use_cursor = false, after_cursor = false;
|
||||
_cleanup_(sd_journal_closep) sd_journal *j = NULL;
|
||||
sd_id128_t previous_boot_id = {}; /* Unnecessary initialization to appease gcc */
|
||||
sd_id128_t previous_boot_id = SD_ID128_NULL, previous_boot_id_output = SD_ID128_NULL;
|
||||
dual_timestamp previous_ts_output = DUAL_TIMESTAMP_NULL;
|
||||
int n_shown = 0, r, poll_fd = -1;
|
||||
|
||||
setlocale(LC_ALL, "");
|
||||
@ -2673,7 +2674,8 @@ int main(int argc, char *argv[]) {
|
||||
arg_no_hostname * OUTPUT_NO_HOSTNAME;
|
||||
|
||||
r = show_journal_entry(stdout, j, arg_output, 0, flags,
|
||||
arg_output_fields, highlight, &ellipsized);
|
||||
arg_output_fields, highlight, &ellipsized,
|
||||
&previous_ts_output, &previous_boot_id_output);
|
||||
need_seek = true;
|
||||
if (r == -EADDRNOTAVAIL)
|
||||
break;
|
||||
|
@ -1267,9 +1267,9 @@ static int help(int argc, char *argv[], void *userdata) {
|
||||
" -n --lines=INTEGER Number of journal entries to show\n"
|
||||
" -o --output=STRING Change journal output mode (short, short-precise,\n"
|
||||
" short-iso, short-iso-precise, short-full,\n"
|
||||
" short-monotonic, short-unix, verbose, export,\n"
|
||||
" short-monotonic, short-unix, short-delta,\n"
|
||||
" json, json-pretty, json-sse, json-seq, cat,\n"
|
||||
" with-unit)\n"
|
||||
" verbose, export, with-unit)\n"
|
||||
"\nSee the %s for details.\n",
|
||||
program_invocation_short_name,
|
||||
ansi_highlight(),
|
||||
|
@ -2462,9 +2462,9 @@ static int help(int argc, char *argv[], void *userdata) {
|
||||
" --max-addresses=INTEGER Number of internet addresses to show at most\n"
|
||||
" -o --output=STRING Change journal output mode (short, short-precise,\n"
|
||||
" short-iso, short-iso-precise, short-full,\n"
|
||||
" short-monotonic, short-unix, verbose, export,\n"
|
||||
" short-monotonic, short-unix, short-delta,\n"
|
||||
" json, json-pretty, json-sse, json-seq, cat,\n"
|
||||
" with-unit)\n"
|
||||
" verbose, export, with-unit)\n"
|
||||
" --verify=MODE Verification mode for downloaded images (no,\n"
|
||||
" checksum, signature)\n"
|
||||
" --force Download image even if already exists\n"
|
||||
|
@ -317,62 +317,87 @@ static bool print_multiline(
|
||||
return ellipsized;
|
||||
}
|
||||
|
||||
static int output_timestamp_monotonic(FILE *f, sd_journal *j, const char *monotonic) {
|
||||
sd_id128_t boot_id;
|
||||
uint64_t t;
|
||||
int r;
|
||||
static int output_timestamp_monotonic(
|
||||
FILE *f, OutputMode mode,
|
||||
const dual_timestamp *ts,
|
||||
const sd_id128_t *boot_id,
|
||||
const dual_timestamp *previous_ts,
|
||||
const sd_id128_t *previous_boot_id) {
|
||||
|
||||
int written_chars = 0;
|
||||
|
||||
assert(f);
|
||||
assert(j);
|
||||
assert(ts);
|
||||
assert(boot_id);
|
||||
assert(previous_ts);
|
||||
assert(previous_boot_id);
|
||||
|
||||
r = -ENXIO;
|
||||
if (monotonic)
|
||||
r = safe_atou64(monotonic, &t);
|
||||
if (r < 0)
|
||||
r = sd_journal_get_monotonic_usec(j, &t, &boot_id);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to get monotonic timestamp: %m");
|
||||
if (!VALID_MONOTONIC(ts->monotonic))
|
||||
return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "No valid monotonic timestamp available");
|
||||
|
||||
fprintf(f, "[%5"PRI_USEC".%06"PRI_USEC"]", t / USEC_PER_SEC, t % USEC_PER_SEC);
|
||||
return 1 + 5 + 1 + 6 + 1;
|
||||
written_chars += fprintf(f, "[%5"PRI_USEC".%06"PRI_USEC, ts->monotonic / USEC_PER_SEC, ts->monotonic % USEC_PER_SEC);
|
||||
|
||||
if (mode == OUTPUT_SHORT_DELTA) {
|
||||
uint64_t delta;
|
||||
bool reliable_ts = true;
|
||||
|
||||
if (VALID_MONOTONIC(previous_ts->monotonic) && sd_id128_equal(*boot_id, *previous_boot_id))
|
||||
delta = usec_sub_unsigned(ts->monotonic, previous_ts->monotonic);
|
||||
else if (VALID_REALTIME(ts->realtime) && VALID_REALTIME(previous_ts->realtime)) {
|
||||
delta = usec_sub_unsigned(ts->realtime, previous_ts->realtime);
|
||||
reliable_ts = false;
|
||||
} else {
|
||||
written_chars += fprintf(f, "%16s", "");
|
||||
goto finish;
|
||||
}
|
||||
|
||||
written_chars += fprintf(f, " <%5"PRI_USEC".%06"PRI_USEC"%s>", delta / USEC_PER_SEC, delta % USEC_PER_SEC, reliable_ts ? " " : "*");
|
||||
}
|
||||
|
||||
finish:
|
||||
written_chars += fprintf(f, "%s", "]");
|
||||
|
||||
return written_chars;
|
||||
}
|
||||
|
||||
static int output_timestamp_realtime(FILE *f, sd_journal *j, OutputMode mode, OutputFlags flags, const char *realtime) {
|
||||
static int output_timestamp_realtime(
|
||||
FILE *f,
|
||||
sd_journal *j,
|
||||
OutputMode mode,
|
||||
OutputFlags flags,
|
||||
const dual_timestamp *ts) {
|
||||
|
||||
char buf[CONST_MAX(FORMAT_TIMESTAMP_MAX, 64U)];
|
||||
uint64_t x;
|
||||
int r;
|
||||
|
||||
assert(f);
|
||||
assert(j);
|
||||
assert(ts);
|
||||
|
||||
if (realtime)
|
||||
r = safe_atou64(realtime, &x);
|
||||
if (!realtime || r < 0 || !VALID_REALTIME(x))
|
||||
r = sd_journal_get_realtime_usec(j, &x);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to get realtime timestamp: %m");
|
||||
if (!VALID_REALTIME(ts->realtime))
|
||||
return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "No valid realtime timestamp available");
|
||||
|
||||
if (IN_SET(mode, OUTPUT_SHORT_FULL, OUTPUT_WITH_UNIT)) {
|
||||
const char *k;
|
||||
|
||||
if (flags & OUTPUT_UTC)
|
||||
k = format_timestamp_style(buf, sizeof(buf), x, TIMESTAMP_UTC);
|
||||
k = format_timestamp_style(buf, sizeof(buf), ts->realtime, TIMESTAMP_UTC);
|
||||
else
|
||||
k = format_timestamp(buf, sizeof(buf), x);
|
||||
k = format_timestamp(buf, sizeof(buf), ts->realtime);
|
||||
if (!k)
|
||||
return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
|
||||
"Failed to format timestamp: %" PRIu64, x);
|
||||
"Failed to format timestamp: %" PRIu64, ts->realtime);
|
||||
|
||||
} else {
|
||||
struct tm tm;
|
||||
time_t t;
|
||||
|
||||
t = (time_t) (x / USEC_PER_SEC);
|
||||
t = (time_t) (ts->realtime / USEC_PER_SEC);
|
||||
|
||||
switch (mode) {
|
||||
|
||||
case OUTPUT_SHORT_UNIX:
|
||||
xsprintf(buf, "%10"PRI_TIME".%06"PRIu64, t, x % USEC_PER_SEC);
|
||||
xsprintf(buf, "%10"PRI_TIME".%06"PRIu64, t, ts->realtime % USEC_PER_SEC);
|
||||
break;
|
||||
|
||||
case OUTPUT_SHORT_ISO:
|
||||
@ -390,7 +415,7 @@ static int output_timestamp_realtime(FILE *f, sd_journal *j, OutputMode mode, Ou
|
||||
localtime_or_gmtime_r(&t, &tm, flags & OUTPUT_UTC)) <= 0)
|
||||
return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
|
||||
"Failed to format ISO-precise time");
|
||||
xsprintf(usec, "%06"PRI_USEC, x % USEC_PER_SEC);
|
||||
xsprintf(usec, "%06"PRI_USEC, ts->realtime % USEC_PER_SEC);
|
||||
memcpy(buf + 20, usec, 6);
|
||||
break;
|
||||
}
|
||||
@ -408,7 +433,7 @@ static int output_timestamp_realtime(FILE *f, sd_journal *j, OutputMode mode, Ou
|
||||
assert(sizeof(buf) > strlen(buf));
|
||||
k = sizeof(buf) - strlen(buf);
|
||||
|
||||
r = snprintf(buf + strlen(buf), k, ".%06"PRIu64, x % USEC_PER_SEC);
|
||||
r = snprintf(buf + strlen(buf), k, ".%06"PRIu64, ts->realtime % USEC_PER_SEC);
|
||||
if (r <= 0 || (size_t) r >= k) /* too long? */
|
||||
return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
|
||||
"Failed to format precise time");
|
||||
@ -431,16 +456,20 @@ static int output_short(
|
||||
unsigned n_columns,
|
||||
OutputFlags flags,
|
||||
const Set *output_fields,
|
||||
const size_t highlight[2]) {
|
||||
const size_t highlight[2],
|
||||
const dual_timestamp *ts,
|
||||
const sd_id128_t *boot_id,
|
||||
const dual_timestamp *previous_ts,
|
||||
const sd_id128_t *previous_boot_id) {
|
||||
|
||||
int r;
|
||||
const void *data;
|
||||
size_t length, n = 0;
|
||||
_cleanup_free_ char *hostname = NULL, *identifier = NULL, *comm = NULL, *pid = NULL, *fake_pid = NULL,
|
||||
*message = NULL, *realtime = NULL, *monotonic = NULL, *priority = NULL, *transport = NULL,
|
||||
*message = NULL, *priority = NULL, *transport = NULL,
|
||||
*config_file = NULL, *unit = NULL, *user_unit = NULL, *documentation_url = NULL;
|
||||
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, transport_len = 0, config_file_len = 0,
|
||||
priority_len = 0, transport_len = 0, config_file_len = 0,
|
||||
unit_len = 0, user_unit_len = 0, documentation_url_len = 0;
|
||||
int p = LOG_INFO;
|
||||
bool ellipsized = false, audit;
|
||||
@ -453,8 +482,6 @@ static int output_short(
|
||||
PARSE_FIELD_VEC_ENTRY("_HOSTNAME=", &hostname, &hostname_len),
|
||||
PARSE_FIELD_VEC_ENTRY("SYSLOG_PID=", &fake_pid, &fake_pid_len),
|
||||
PARSE_FIELD_VEC_ENTRY("SYSLOG_IDENTIFIER=", &identifier, &identifier_len),
|
||||
PARSE_FIELD_VEC_ENTRY("_SOURCE_REALTIME_TIMESTAMP=", &realtime, &realtime_len),
|
||||
PARSE_FIELD_VEC_ENTRY("_SOURCE_MONOTONIC_TIMESTAMP=", &monotonic, &monotonic_len),
|
||||
PARSE_FIELD_VEC_ENTRY("CONFIG_FILE=", &config_file, &config_file_len),
|
||||
PARSE_FIELD_VEC_ENTRY("_SYSTEMD_UNIT=", &unit, &unit_len),
|
||||
PARSE_FIELD_VEC_ENTRY("_SYSTEMD_USER_UNIT=", &user_unit, &user_unit_len),
|
||||
@ -464,6 +491,10 @@ static int output_short(
|
||||
|
||||
assert(f);
|
||||
assert(j);
|
||||
assert(ts);
|
||||
assert(boot_id);
|
||||
assert(previous_ts);
|
||||
assert(previous_boot_id);
|
||||
|
||||
/* Set the threshold to one bigger than the actual print
|
||||
* threshold, so that if the line is actually longer than what
|
||||
@ -498,10 +529,10 @@ static int output_short(
|
||||
|
||||
audit = streq_ptr(transport, "audit");
|
||||
|
||||
if (mode == OUTPUT_SHORT_MONOTONIC)
|
||||
r = output_timestamp_monotonic(f, j, monotonic);
|
||||
if (IN_SET(mode, OUTPUT_SHORT_MONOTONIC, OUTPUT_SHORT_DELTA))
|
||||
r = output_timestamp_monotonic(f, mode, ts, boot_id, previous_ts, previous_boot_id);
|
||||
else
|
||||
r = output_timestamp_realtime(f, j, mode, flags, realtime);
|
||||
r = output_timestamp_realtime(f, j, mode, flags, ts);
|
||||
if (r < 0)
|
||||
return r;
|
||||
n += r;
|
||||
@ -632,52 +663,36 @@ static int output_verbose(
|
||||
unsigned n_columns,
|
||||
OutputFlags flags,
|
||||
const Set *output_fields,
|
||||
const size_t highlight[2]) {
|
||||
const size_t highlight[2],
|
||||
const dual_timestamp *ts,
|
||||
const sd_id128_t *boot_id,
|
||||
const dual_timestamp *previous_ts,
|
||||
const sd_id128_t *previous_boot_id) {
|
||||
|
||||
const void *data;
|
||||
size_t length;
|
||||
_cleanup_free_ char *cursor = NULL;
|
||||
uint64_t realtime = 0;
|
||||
char ts[FORMAT_TIMESTAMP_MAX + 7];
|
||||
char buf[FORMAT_TIMESTAMP_MAX + 7];
|
||||
const char *timestamp;
|
||||
int r;
|
||||
|
||||
assert(f);
|
||||
assert(j);
|
||||
assert(ts);
|
||||
assert(boot_id);
|
||||
assert(previous_ts);
|
||||
assert(previous_boot_id);
|
||||
|
||||
sd_journal_set_data_threshold(j, 0);
|
||||
|
||||
r = sd_journal_get_data(j, "_SOURCE_REALTIME_TIMESTAMP", &data, &length);
|
||||
if (r == -ENOENT)
|
||||
log_debug("Source realtime timestamp not found");
|
||||
else if (r < 0)
|
||||
return log_full_errno(r == -EADDRNOTAVAIL ? LOG_DEBUG : LOG_ERR, r, "Failed to get source realtime timestamp: %m");
|
||||
else {
|
||||
_cleanup_free_ char *value = NULL;
|
||||
|
||||
r = parse_field(data, length, "_SOURCE_REALTIME_TIMESTAMP=",
|
||||
STRLEN("_SOURCE_REALTIME_TIMESTAMP="), &value,
|
||||
NULL);
|
||||
if (r < 0)
|
||||
return r;
|
||||
assert(r > 0);
|
||||
|
||||
r = safe_atou64(value, &realtime);
|
||||
if (r < 0)
|
||||
log_debug_errno(r, "Failed to parse realtime timestamp: %m");
|
||||
}
|
||||
|
||||
if (r < 0) {
|
||||
r = sd_journal_get_realtime_usec(j, &realtime);
|
||||
if (r < 0)
|
||||
return log_full_errno(r == -EADDRNOTAVAIL ? LOG_DEBUG : LOG_ERR, r, "Failed to get realtime timestamp: %m");
|
||||
}
|
||||
if (!VALID_REALTIME(ts->realtime))
|
||||
return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "No valid realtime timestamp available");
|
||||
|
||||
r = sd_journal_get_cursor(j, &cursor);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to get cursor: %m");
|
||||
|
||||
timestamp = format_timestamp_style(ts, sizeof ts, realtime,
|
||||
timestamp = format_timestamp_style(buf, sizeof buf, ts->realtime,
|
||||
flags & OUTPUT_UTC ? TIMESTAMP_US_UTC : TIMESTAMP_US);
|
||||
fprintf(f, "%s [%s]\n",
|
||||
timestamp ?: "(no timestamp)",
|
||||
@ -750,26 +765,30 @@ static int output_export(
|
||||
unsigned n_columns,
|
||||
OutputFlags flags,
|
||||
const Set *output_fields,
|
||||
const size_t highlight[2]) {
|
||||
const size_t highlight[2],
|
||||
const dual_timestamp *ts,
|
||||
const sd_id128_t *boot_id,
|
||||
const dual_timestamp *previous_ts,
|
||||
const sd_id128_t *previous_boot_id) {
|
||||
|
||||
_cleanup_free_ char *cursor = NULL;
|
||||
usec_t realtime, monotonic;
|
||||
sd_id128_t boot_id;
|
||||
const void *data;
|
||||
size_t length;
|
||||
int r;
|
||||
|
||||
assert(j);
|
||||
assert(ts);
|
||||
assert(boot_id);
|
||||
assert(previous_ts);
|
||||
assert(previous_boot_id);
|
||||
|
||||
sd_journal_set_data_threshold(j, 0);
|
||||
|
||||
r = sd_journal_get_realtime_usec(j, &realtime);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to get realtime timestamp: %m");
|
||||
if (!VALID_REALTIME(ts->realtime))
|
||||
return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "No valid realtime timestamp available");
|
||||
|
||||
r = sd_journal_get_monotonic_usec(j, &monotonic, &boot_id);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to get monotonic timestamp: %m");
|
||||
if (!VALID_MONOTONIC(ts->monotonic))
|
||||
return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "No valid monotonic timestamp available");
|
||||
|
||||
r = sd_journal_get_cursor(j, &cursor);
|
||||
if (r < 0)
|
||||
@ -781,9 +800,9 @@ static int output_export(
|
||||
"__MONOTONIC_TIMESTAMP="USEC_FMT"\n"
|
||||
"_BOOT_ID=%s\n",
|
||||
cursor,
|
||||
realtime,
|
||||
monotonic,
|
||||
SD_ID128_TO_STRING(boot_id));
|
||||
ts->realtime,
|
||||
ts->monotonic,
|
||||
SD_ID128_TO_STRING(*boot_id));
|
||||
|
||||
JOURNAL_FOREACH_DATA_RETVAL(j, data, length, r) {
|
||||
size_t fieldlen;
|
||||
@ -985,30 +1004,34 @@ static int output_json(
|
||||
unsigned n_columns,
|
||||
OutputFlags flags,
|
||||
const Set *output_fields,
|
||||
const size_t highlight[2]) {
|
||||
const size_t highlight[2],
|
||||
const dual_timestamp *ts,
|
||||
const sd_id128_t *boot_id,
|
||||
const dual_timestamp *previous_ts,
|
||||
const sd_id128_t *previous_boot_id) {
|
||||
|
||||
char sid[SD_ID128_STRING_MAX], usecbuf[DECIMAL_STR_MAX(usec_t)];
|
||||
_cleanup_(json_variant_unrefp) JsonVariant *object = NULL;
|
||||
_cleanup_free_ char *cursor = NULL;
|
||||
uint64_t realtime, monotonic;
|
||||
JsonVariant **array = NULL;
|
||||
struct json_data *d;
|
||||
sd_id128_t boot_id;
|
||||
Hashmap *h = NULL;
|
||||
size_t n = 0;
|
||||
int r;
|
||||
|
||||
assert(j);
|
||||
assert(ts);
|
||||
assert(boot_id);
|
||||
assert(previous_ts);
|
||||
assert(previous_boot_id);
|
||||
|
||||
(void) sd_journal_set_data_threshold(j, flags & OUTPUT_SHOW_ALL ? 0 : JSON_THRESHOLD);
|
||||
|
||||
r = sd_journal_get_realtime_usec(j, &realtime);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to get realtime timestamp: %m");
|
||||
if (!VALID_REALTIME(ts->realtime))
|
||||
return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "No valid realtime timestamp available");
|
||||
|
||||
r = sd_journal_get_monotonic_usec(j, &monotonic, &boot_id);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to get monotonic timestamp: %m");
|
||||
if (!VALID_MONOTONIC(ts->monotonic))
|
||||
return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "No valid monotonic timestamp available");
|
||||
|
||||
r = sd_journal_get_cursor(j, &cursor);
|
||||
if (r < 0)
|
||||
@ -1022,17 +1045,17 @@ static int output_json(
|
||||
if (r < 0)
|
||||
goto finish;
|
||||
|
||||
xsprintf(usecbuf, USEC_FMT, realtime);
|
||||
xsprintf(usecbuf, USEC_FMT, ts->realtime);
|
||||
r = update_json_data(h, flags, "__REALTIME_TIMESTAMP", usecbuf, strlen(usecbuf));
|
||||
if (r < 0)
|
||||
goto finish;
|
||||
|
||||
xsprintf(usecbuf, USEC_FMT, monotonic);
|
||||
xsprintf(usecbuf, USEC_FMT, ts->monotonic);
|
||||
r = update_json_data(h, flags, "__MONOTONIC_TIMESTAMP", usecbuf, strlen(usecbuf));
|
||||
if (r < 0)
|
||||
goto finish;
|
||||
|
||||
sd_id128_to_string(boot_id, sid);
|
||||
sd_id128_to_string(*boot_id, sid);
|
||||
r = update_json_data(h, flags, "_BOOT_ID", sid, strlen(sid));
|
||||
if (r < 0)
|
||||
goto finish;
|
||||
@ -1181,13 +1204,21 @@ static int output_cat(
|
||||
unsigned n_columns,
|
||||
OutputFlags flags,
|
||||
const Set *output_fields,
|
||||
const size_t highlight[2]) {
|
||||
const size_t highlight[2],
|
||||
const dual_timestamp *ts,
|
||||
const sd_id128_t *boot_id,
|
||||
const dual_timestamp *previous_ts,
|
||||
const sd_id128_t *previous_boot_id) {
|
||||
|
||||
int r, prio = LOG_INFO;
|
||||
const char *field;
|
||||
|
||||
assert(j);
|
||||
assert(f);
|
||||
assert(ts);
|
||||
assert(boot_id);
|
||||
assert(previous_ts);
|
||||
assert(previous_boot_id);
|
||||
|
||||
(void) sd_journal_set_data_threshold(j, 0);
|
||||
|
||||
@ -1227,6 +1258,50 @@ static int output_cat(
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int get_dual_timestamp(sd_journal *j, dual_timestamp *ret_ts, sd_id128_t *ret_boot_id) {
|
||||
const void *data;
|
||||
_cleanup_free_ char *realtime = NULL, *monotonic = NULL;
|
||||
size_t length = 0, realtime_len = 0, monotonic_len = 0;
|
||||
const ParseFieldVec message_fields[] = {
|
||||
PARSE_FIELD_VEC_ENTRY("_SOURCE_REALTIME_TIMESTAMP=", &realtime, &realtime_len),
|
||||
PARSE_FIELD_VEC_ENTRY("_SOURCE_MONOTONIC_TIMESTAMP=", &monotonic, &monotonic_len),
|
||||
};
|
||||
int r;
|
||||
|
||||
assert(j);
|
||||
assert(ret_ts);
|
||||
assert(ret_boot_id);
|
||||
|
||||
JOURNAL_FOREACH_DATA_RETVAL(j, data, length, r) {
|
||||
r = parse_fieldv(data, length, message_fields, ELEMENTSOF(message_fields));
|
||||
if (r < 0)
|
||||
return r;
|
||||
}
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
if (realtime)
|
||||
r = safe_atou64(realtime, &ret_ts->realtime);
|
||||
if (!realtime || r < 0 || !VALID_REALTIME(ret_ts->realtime))
|
||||
r = sd_journal_get_realtime_usec(j, &ret_ts->realtime);
|
||||
if (r < 0)
|
||||
ret_ts->realtime = USEC_INFINITY;
|
||||
|
||||
if (monotonic)
|
||||
r = safe_atou64(monotonic, &ret_ts->monotonic);
|
||||
if (!monotonic || r < 0 || !VALID_MONOTONIC(ret_ts->monotonic))
|
||||
r = sd_journal_get_monotonic_usec(j, &ret_ts->monotonic, ret_boot_id);
|
||||
if (r < 0)
|
||||
ret_ts->monotonic = USEC_INFINITY;
|
||||
|
||||
/* Restart all data before */
|
||||
sd_journal_restart_data(j);
|
||||
sd_journal_restart_unique(j);
|
||||
sd_journal_restart_fields(j);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int (*output_funcs[_OUTPUT_MODE_MAX])(
|
||||
FILE *f,
|
||||
sd_journal *j,
|
||||
@ -1234,13 +1309,18 @@ static int (*output_funcs[_OUTPUT_MODE_MAX])(
|
||||
unsigned n_columns,
|
||||
OutputFlags flags,
|
||||
const Set *output_fields,
|
||||
const size_t highlight[2]) = {
|
||||
const size_t highlight[2],
|
||||
const dual_timestamp *ts,
|
||||
const sd_id128_t *boot_id,
|
||||
const dual_timestamp *previous_ts,
|
||||
const sd_id128_t *previous_boot_id) = {
|
||||
|
||||
[OUTPUT_SHORT] = output_short,
|
||||
[OUTPUT_SHORT_ISO] = output_short,
|
||||
[OUTPUT_SHORT_ISO_PRECISE] = output_short,
|
||||
[OUTPUT_SHORT_PRECISE] = output_short,
|
||||
[OUTPUT_SHORT_MONOTONIC] = output_short,
|
||||
[OUTPUT_SHORT_DELTA] = output_short,
|
||||
[OUTPUT_SHORT_UNIX] = output_short,
|
||||
[OUTPUT_SHORT_FULL] = output_short,
|
||||
[OUTPUT_VERBOSE] = output_verbose,
|
||||
@ -1261,13 +1341,19 @@ int show_journal_entry(
|
||||
OutputFlags flags,
|
||||
char **output_fields,
|
||||
const size_t highlight[2],
|
||||
bool *ellipsized) {
|
||||
bool *ellipsized,
|
||||
dual_timestamp *previous_ts,
|
||||
sd_id128_t *previous_boot_id) {
|
||||
|
||||
_cleanup_set_free_ Set *fields = NULL;
|
||||
dual_timestamp ts = DUAL_TIMESTAMP_NULL;
|
||||
sd_id128_t boot_id = SD_ID128_NULL;
|
||||
int r;
|
||||
|
||||
assert(mode >= 0);
|
||||
assert(mode < _OUTPUT_MODE_MAX);
|
||||
assert(previous_ts);
|
||||
assert(previous_boot_id);
|
||||
|
||||
if (n_columns <= 0)
|
||||
n_columns = columns();
|
||||
@ -1276,7 +1362,19 @@ int show_journal_entry(
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = output_funcs[mode](f, j, mode, n_columns, flags, fields, highlight);
|
||||
r = get_dual_timestamp(j, &ts, &boot_id);
|
||||
if (r == -EBADMSG) {
|
||||
log_debug_errno(r, "Skipping message we can't read: %m");
|
||||
return 0;
|
||||
}
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to get journal fields: %m");
|
||||
|
||||
r = output_funcs[mode](f, j, mode, n_columns, flags, fields, highlight, &ts, &boot_id, previous_ts, previous_boot_id);
|
||||
|
||||
/* Store timestamp and boot ID for next iteration */
|
||||
*previous_ts = ts;
|
||||
*previous_boot_id = boot_id;
|
||||
|
||||
if (ellipsized && r > 0)
|
||||
*ellipsized = true;
|
||||
@ -1313,6 +1411,8 @@ int show_journal(
|
||||
unsigned line = 0;
|
||||
bool need_seek = false;
|
||||
int warn_cutoff = flags & OUTPUT_WARN_CUTOFF;
|
||||
dual_timestamp previous_ts = DUAL_TIMESTAMP_NULL;
|
||||
sd_id128_t previous_boot_id = SD_ID128_NULL;
|
||||
|
||||
assert(j);
|
||||
assert(mode >= 0);
|
||||
@ -1361,7 +1461,8 @@ int show_journal(
|
||||
line++;
|
||||
maybe_print_begin_newline(f, &flags);
|
||||
|
||||
r = show_journal_entry(f, j, mode, n_columns, flags, NULL, NULL, ellipsized);
|
||||
r = show_journal_entry(f, j, mode, n_columns, flags, NULL, NULL, ellipsized,
|
||||
&previous_ts, &previous_boot_id);
|
||||
if (r < 0)
|
||||
return r;
|
||||
}
|
||||
|
@ -21,7 +21,9 @@ int show_journal_entry(
|
||||
OutputFlags flags,
|
||||
char **output_fields,
|
||||
const size_t highlight[2],
|
||||
bool *ellipsized);
|
||||
bool *ellipsized,
|
||||
dual_timestamp *previous_ts,
|
||||
sd_id128_t *previous_boot_id);
|
||||
int show_journal(
|
||||
FILE *f,
|
||||
sd_journal *j,
|
||||
|
@ -28,6 +28,7 @@ static const char *const output_mode_table[_OUTPUT_MODE_MAX] = {
|
||||
[OUTPUT_SHORT_ISO_PRECISE] = "short-iso-precise",
|
||||
[OUTPUT_SHORT_PRECISE] = "short-precise",
|
||||
[OUTPUT_SHORT_MONOTONIC] = "short-monotonic",
|
||||
[OUTPUT_SHORT_DELTA] = "short-delta",
|
||||
[OUTPUT_SHORT_UNIX] = "short-unix",
|
||||
[OUTPUT_VERBOSE] = "verbose",
|
||||
[OUTPUT_EXPORT] = "export",
|
||||
|
@ -11,6 +11,7 @@ typedef enum OutputMode {
|
||||
OUTPUT_SHORT_ISO_PRECISE,
|
||||
OUTPUT_SHORT_PRECISE,
|
||||
OUTPUT_SHORT_MONOTONIC,
|
||||
OUTPUT_SHORT_DELTA,
|
||||
OUTPUT_SHORT_UNIX,
|
||||
OUTPUT_VERBOSE,
|
||||
OUTPUT_EXPORT,
|
||||
|
@ -291,7 +291,7 @@ static int systemctl_help(void) {
|
||||
" -n --lines=INTEGER Number of journal entries to show\n"
|
||||
" -o --output=STRING Change journal output mode (short, short-precise,\n"
|
||||
" short-iso, short-iso-precise, short-full,\n"
|
||||
" short-monotonic, short-unix,\n"
|
||||
" short-monotonic, short-unix, short-delta,\n"
|
||||
" verbose, export, json, json-pretty, json-sse, cat)\n"
|
||||
" --firmware-setup Tell the firmware to show the setup menu on next boot\n"
|
||||
" --boot-loader-menu=TIME\n"
|
||||
|
Loading…
x
Reference in New Issue
Block a user