journalctl: add new output mode "short-full" (#3880)

This new output mode formats all timestamps using the usual format_timestamp()
call we use pretty much everywhere else. Timestamps formatted this way are some
ways more useful than traditional syslog timestamps as they include weekday,
month and timezone information, while not being much longer. They are also not
locale-dependent. The primary advantage however is that they may be passed
directly to journalctl's --since= and --until= switches as soon as #3869 is
merged.

While we are at it, let's also add "short-unix" to shell completion.
This commit is contained in:
Lennart Poettering 2016-08-04 01:45:07 +02:00 committed by Zbigniew Jędrzejewski-Szmek
parent 21b3a0fcd1
commit 29a753df76
7 changed files with 140 additions and 92 deletions

View file

@ -248,6 +248,18 @@
</listitem>
</varlistentry>
<varlistentry>
<term>
<option>short-full</option>
</term>
<listitem>
<para>is very similar, but shows timestamps in the format the <option>--since=</option> and
<option>--until=</option> options accept. Unlike the timestamp information shown in
<option>short</option> output mode this mode includes weekday, year and timezone information in the
output, and is locale-independent.</para>
</listitem>
</varlistentry>
<varlistentry>
<term>
<option>short-iso</option>
@ -572,24 +584,17 @@
<term><option>-U</option></term>
<term><option>--until=</option></term>
<listitem><para>Start showing entries on or newer than the
specified date, or on or older than the specified date,
respectively. Date specifications should be of the format
<literal>2012-10-30 18:17:16</literal>. If the time part is
omitted, <literal>00:00:00</literal> is assumed. If only the
seconds component is omitted, <literal>:00</literal> is
assumed. If the date component is omitted, the current day is
assumed. Alternatively the strings
<literal>yesterday</literal>, <literal>today</literal>,
<literal>tomorrow</literal> are understood, which refer to
00:00:00 of the day before the current day, the current day,
or the day after the current day,
respectively. <literal>now</literal> refers to the current
time. Finally, relative times may be specified, prefixed with
<literal>-</literal> or <literal>+</literal>, referring to
times before or after the current time, respectively. For complete
time and date specification, see
<citerefentry><refentrytitle>systemd.time</refentrytitle><manvolnum>7</manvolnum></citerefentry>.
<listitem><para>Start showing entries on or newer than the specified date, or on or older than the specified
date, respectively. Date specifications should be of the format <literal>2012-10-30 18:17:16</literal>. If the
time part is omitted, <literal>00:00:00</literal> is assumed. If only the seconds component is omitted,
<literal>:00</literal> is assumed. If the date component is omitted, the current day is assumed. Alternatively
the strings <literal>yesterday</literal>, <literal>today</literal>, <literal>tomorrow</literal> are understood,
which refer to 00:00:00 of the day before the current day, the current day, or the day after the current day,
respectively. <literal>now</literal> refers to the current time. Finally, relative times may be specified,
prefixed with <literal>-</literal> or <literal>+</literal>, referring to times before or after the current
time, respectively. For complete time and date specification, see
<citerefentry><refentrytitle>systemd.time</refentrytitle><manvolnum>7</manvolnum></citerefentry>. Note that
<option>--output=short-full</option> prints timestamps that follow precisely this format.
</para>
</listitem>
</varlistentry>

View file

@ -65,7 +65,7 @@ _journalctl() {
compopt -o filenames
;;
--output|-o)
comps='short short-iso short-precise short-monotonic verbose export json json-pretty json-sse cat'
comps='short short-full short-iso short-precise short-monotonic short-unix verbose export json json-pretty json-sse cat'
;;
--field|-F)
comps=$(journalctl --fields | sort 2>/dev/null)

View file

@ -145,7 +145,7 @@ _systemctl () {
comps='full enable-only disable-only'
;;
--output|-o)
comps='short short-iso short-precise short-monotonic verbose export json
comps='short short-full short-iso short-precise short-monotonic short-unix verbose export json
json-pretty json-sse cat'
;;
--machine|-M)

View file

@ -1,5 +1,5 @@
#autoload
local -a _output_opts
_output_opts=(short short-iso short-precise short-monotonic verbose export json json-pretty json-sse cat)
_output_opts=(short short-full short-iso short-precise short-monotonic short-unix verbose export json json-pretty json-sse cat)
_describe -t output 'output mode' _output_opts || compadd "$@"

View file

@ -45,6 +45,7 @@
#include "parse-util.h"
#include "process-util.h"
#include "sparse-endian.h"
#include "stdio-util.h"
#include "string-table.h"
#include "string-util.h"
#include "terminal-util.h"
@ -206,6 +207,108 @@ static bool print_multiline(FILE *f, unsigned prefix, unsigned n_columns, Output
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;
assert(f);
assert(j);
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");
fprintf(f, "[%5llu.%06llu]",
(unsigned long long) (t / USEC_PER_SEC),
(unsigned long long) (t % USEC_PER_SEC));
return 1 + 5 + 1 + 6 + 1;
}
static int output_timestamp_realtime(FILE *f, sd_journal *j, OutputMode mode, OutputFlags flags, const char *realtime) {
char buf[MAX(FORMAT_TIMESTAMP_MAX, 64)];
struct tm *(*gettime_r)(const time_t *, struct tm *);
struct tm tm;
uint64_t x;
time_t t;
int r;
assert(f);
assert(j);
r = -ENXIO;
if (realtime)
r = safe_atou64(realtime, &x);
if (r < 0)
r = sd_journal_get_realtime_usec(j, &x);
if (r < 0)
return log_error_errno(r, "Failed to get realtime timestamp: %m");
if (mode == OUTPUT_SHORT_FULL) {
const char *k;
if (flags & OUTPUT_UTC)
k = format_timestamp_utc(buf, sizeof(buf), x);
else
k = format_timestamp(buf, sizeof(buf), x);
if (!k) {
log_error("Failed to format timestamp.");
return -EINVAL;
}
} else {
gettime_r = (flags & OUTPUT_UTC) ? gmtime_r : localtime_r;
t = (time_t) (x / USEC_PER_SEC);
switch (mode) {
case OUTPUT_SHORT_UNIX:
xsprintf(buf, "%10llu.%06llu", (unsigned long long) t, (unsigned long long) (x % USEC_PER_SEC));
break;
case OUTPUT_SHORT_ISO:
if (strftime(buf, sizeof(buf), "%Y-%m-%dT%H:%M:%S%z", gettime_r(&t, &tm)) <= 0) {
log_error("Failed for format ISO time");
return -EINVAL;
}
break;
case OUTPUT_SHORT:
case OUTPUT_SHORT_PRECISE:
if (strftime(buf, sizeof(buf), "%b %d %H:%M:%S", gettime_r(&t, &tm)) <= 0) {
log_error("Failed to format syslog time");
return -EINVAL;
}
if (mode == OUTPUT_SHORT_PRECISE) {
size_t k;
assert(sizeof(buf) > strlen(buf));
k = sizeof(buf) - strlen(buf);
r = snprintf(buf + strlen(buf), k, ".%06llu", (unsigned long long) (x % USEC_PER_SEC));
if (r <= 0 || (size_t) r >= k) { /* too long? */
log_error("Failed to format precise time");
return -EINVAL;
}
}
break;
default:
assert_not_reached("Unknown time format");
}
}
fputs(buf, f);
return (int) strlen(buf);
}
static int output_short(
FILE *f,
sd_journal *j,
@ -305,78 +408,15 @@ static int output_short(
if (priority_len == 1 && *priority >= '0' && *priority <= '7')
p = *priority - '0';
if (mode == OUTPUT_SHORT_MONOTONIC) {
uint64_t t;
sd_id128_t boot_id;
if (mode == OUTPUT_SHORT_MONOTONIC)
r = output_timestamp_monotonic(f, j, monotonic);
else
r = output_timestamp_realtime(f, j, mode, flags, realtime);
if (r < 0)
return r;
n += r;
r = -ENOENT;
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");
fprintf(f, "[%5llu.%06llu]",
(unsigned long long) (t / USEC_PER_SEC),
(unsigned long long) (t % USEC_PER_SEC));
n += 1 + 5 + 1 + 6 + 1;
} else {
char buf[64];
uint64_t x;
time_t t;
struct tm tm;
struct tm *(*gettime_r)(const time_t *, struct tm *);
r = -ENOENT;
gettime_r = (flags & OUTPUT_UTC) ? gmtime_r : localtime_r;
if (realtime)
r = safe_atou64(realtime, &x);
if (r < 0)
r = sd_journal_get_realtime_usec(j, &x);
if (r < 0)
return log_error_errno(r, "Failed to get realtime timestamp: %m");
t = (time_t) (x / USEC_PER_SEC);
switch (mode) {
case OUTPUT_SHORT_UNIX:
r = snprintf(buf, sizeof(buf), "%10llu.%06llu", (unsigned long long) t, (unsigned long long) (x % USEC_PER_SEC));
break;
case OUTPUT_SHORT_ISO:
r = strftime(buf, sizeof(buf), "%Y-%m-%dT%H:%M:%S%z", gettime_r(&t, &tm));
break;
case OUTPUT_SHORT_PRECISE:
r = strftime(buf, sizeof(buf), "%b %d %H:%M:%S", gettime_r(&t, &tm));
if (r > 0)
snprintf(buf + strlen(buf), sizeof(buf) - strlen(buf), ".%06llu", (unsigned long long) (x % USEC_PER_SEC));
break;
default:
r = strftime(buf, sizeof(buf), "%b %d %H:%M:%S", gettime_r(&t, &tm));
}
if (r <= 0) {
log_error("Failed to format time.");
return -EINVAL;
}
fputs(buf, f);
n += strlen(buf);
}
if (hostname && (flags & OUTPUT_NO_HOSTNAME)) {
if (flags & OUTPUT_NO_HOSTNAME) {
/* Suppress display of the hostname if this is requested. */
hostname = NULL;
hostname_len = 0;
@ -910,6 +950,7 @@ static int (*output_funcs[_OUTPUT_MODE_MAX])(
[OUTPUT_SHORT_PRECISE] = output_short,
[OUTPUT_SHORT_MONOTONIC] = output_short,
[OUTPUT_SHORT_UNIX] = output_short,
[OUTPUT_SHORT_FULL] = output_short,
[OUTPUT_VERBOSE] = output_verbose,
[OUTPUT_EXPORT] = output_export,
[OUTPUT_JSON] = output_json,

View file

@ -22,6 +22,7 @@
static const char *const output_mode_table[_OUTPUT_MODE_MAX] = {
[OUTPUT_SHORT] = "short",
[OUTPUT_SHORT_FULL] = "short-full",
[OUTPUT_SHORT_ISO] = "short-iso",
[OUTPUT_SHORT_PRECISE] = "short-precise",
[OUTPUT_SHORT_MONOTONIC] = "short-monotonic",

View file

@ -23,6 +23,7 @@
typedef enum OutputMode {
OUTPUT_SHORT,
OUTPUT_SHORT_FULL,
OUTPUT_SHORT_ISO,
OUTPUT_SHORT_PRECISE,
OUTPUT_SHORT_MONOTONIC,