Merge pull request #15915 from poettering/journal-external-link
journal: show external links in log output
This commit is contained in:
@@ -302,7 +302,8 @@ shut down.
|
||||
Subject: DNSSEC mode has been turned off, as server doesn't support it
|
||||
Defined-By: systemd
|
||||
Support: %SUPPORT_URL%
|
||||
Documentation: man:systemd-resolved.service(8) resolved.conf(5)
|
||||
Documentation: man:systemd-resolved.service(8)
|
||||
Documentation: man:resolved.conf(5)
|
||||
|
||||
The resolver service (systemd-resolved.service) has detected that the
|
||||
configured DNS server does not support DNSSEC, and DNSSEC validation has been
|
||||
@@ -417,6 +418,7 @@ Note that the memory pressure might or might not have been caused by @UNIT@.
|
||||
Subject: Accepting user/group name @USER_GROUP_NAME@, which does not match strict user/group name rules.
|
||||
Defined-By: systemd
|
||||
Support: %SUPPORT_URL%
|
||||
Documentation: https://systemd.io/USER_NAMES
|
||||
|
||||
The user/group name @USER_GROUP_NAME@ has been specified, which is accepted
|
||||
according the relaxed user/group name rules, but does not qualify under the
|
||||
@@ -432,10 +434,6 @@ characters; names not valid UTF-8; names with leading or trailing whitespace;
|
||||
the strings "." or ".."; fully numeric strings, or strings beginning in a
|
||||
hyphen and otherwise fully numeric.
|
||||
|
||||
For further details on strict and relaxed user/group name rules, see:
|
||||
|
||||
https://systemd.io/USER_NAMES
|
||||
|
||||
-- 1b3bb94037f04bbf81028e135a12d293
|
||||
Subject: Failed to generate valid unit name from path '@MOUNT_POINT@'.
|
||||
Defined-By: systemd
|
||||
|
||||
@@ -139,6 +139,16 @@
|
||||
</para>
|
||||
</listitem>
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry>
|
||||
<term><varname>DOCUMENTATION=</varname></term>
|
||||
<listitem>
|
||||
<para>A documentation URL with further information about the topic of the log message. Tools such
|
||||
as <command>journalctl</command> will include a hyperlink to an URL specified this way in their
|
||||
output. Should be a <literal>http://</literal>, <literal>https://</literal>,
|
||||
<literal>file:/</literal>, <literal>man:</literal> or <literal>info:</literal> URL.</para>
|
||||
</listitem>
|
||||
</varlistentry>
|
||||
</variablelist>
|
||||
</refsect1>
|
||||
|
||||
|
||||
@@ -365,6 +365,7 @@ const char *special_glyph(SpecialGlyph code) {
|
||||
[SPECIAL_GLYPH_SIGMA] = "S",
|
||||
[SPECIAL_GLYPH_ARROW] = "->",
|
||||
[SPECIAL_GLYPH_ELLIPSIS] = "...",
|
||||
[SPECIAL_GLYPH_EXTERNAL_LINK] = "[LNK]",
|
||||
[SPECIAL_GLYPH_ECSTATIC_SMILEY] = ":-]",
|
||||
[SPECIAL_GLYPH_HAPPY_SMILEY] = ":-}",
|
||||
[SPECIAL_GLYPH_SLIGHTLY_HAPPY_SMILEY] = ":-)",
|
||||
@@ -400,6 +401,9 @@ const char *special_glyph(SpecialGlyph code) {
|
||||
/* Single glyph in Unicode, three in ASCII */
|
||||
[SPECIAL_GLYPH_ELLIPSIS] = "\342\200\246", /* … (actually called: HORIZONTAL ELLIPSIS) */
|
||||
|
||||
/* Three glyphs in Unicode, five in ASCII */
|
||||
[SPECIAL_GLYPH_EXTERNAL_LINK] = "[\360\237\241\225]", /* 🡕 (actually called: NORTH EAST SANS-SERIF ARROW, enclosed in []) */
|
||||
|
||||
/* These smileys are a single glyph in Unicode, and three in ASCII */
|
||||
[SPECIAL_GLYPH_ECSTATIC_SMILEY] = "\360\237\230\207", /* 😇 (actually called: SMILING FACE WITH HALO) */
|
||||
[SPECIAL_GLYPH_HAPPY_SMILEY] = "\360\237\230\200", /* 😀 (actually called: GRINNING FACE) */
|
||||
|
||||
@@ -55,6 +55,7 @@ typedef enum {
|
||||
SPECIAL_GLYPH_LIGHT_SHADE,
|
||||
SPECIAL_GLYPH_DARK_SHADE,
|
||||
SPECIAL_GLYPH_SIGMA,
|
||||
SPECIAL_GLYPH_EXTERNAL_LINK,
|
||||
_SPECIAL_GLYPH_FIRST_EMOJI,
|
||||
SPECIAL_GLYPH_ECSTATIC_SMILEY = _SPECIAL_GLYPH_FIRST_EMOJI,
|
||||
SPECIAL_GLYPH_HAPPY_SMILEY,
|
||||
|
||||
+93
-13
@@ -22,6 +22,7 @@
|
||||
#include "journal-internal.h"
|
||||
#include "journal-util.h"
|
||||
#include "json.h"
|
||||
#include "locale-util.h"
|
||||
#include "log.h"
|
||||
#include "logs-show.h"
|
||||
#include "macro.h"
|
||||
@@ -39,6 +40,7 @@
|
||||
#include "time-util.h"
|
||||
#include "utf8.h"
|
||||
#include "util.h"
|
||||
#include "web-util.h"
|
||||
|
||||
/* up to three lines (each up to 100 characters) or 300 characters, whichever is less */
|
||||
#define PRINT_LINE_THRESHOLD 3
|
||||
@@ -47,12 +49,16 @@
|
||||
#define JSON_THRESHOLD 4096U
|
||||
|
||||
static int print_catalog(FILE *f, sd_journal *j) {
|
||||
int r;
|
||||
_cleanup_free_ char *t = NULL, *z = NULL;
|
||||
int r;
|
||||
|
||||
assert(j);
|
||||
|
||||
r = sd_journal_get_catalog(j, &t);
|
||||
if (r == -ENOENT)
|
||||
return 0;
|
||||
if (r < 0)
|
||||
return r;
|
||||
return log_error_errno(r, "Failed to find catalog entry: %m");
|
||||
|
||||
z = strreplace(strstrip(t), "\n", "\n-- ");
|
||||
if (!z)
|
||||
@@ -62,6 +68,48 @@ static int print_catalog(FILE *f, sd_journal *j) {
|
||||
fputs(z, f);
|
||||
fputc('\n', f);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int url_from_catalog(sd_journal *j, char **ret) {
|
||||
_cleanup_free_ char *t = NULL, *url = NULL;
|
||||
const char *weblink;
|
||||
int r;
|
||||
|
||||
assert(j);
|
||||
assert(ret);
|
||||
|
||||
r = sd_journal_get_catalog(j, &t);
|
||||
if (r == -ENOENT)
|
||||
goto notfound;
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to find catalog entry: %m");
|
||||
|
||||
weblink = startswith(t, "Documentation:");
|
||||
if (!weblink) {
|
||||
weblink = strstr(t + 1, "\nDocumentation:");
|
||||
if (!weblink)
|
||||
goto notfound;
|
||||
|
||||
weblink += 15;
|
||||
}
|
||||
|
||||
/* Skip whitespace to value */
|
||||
weblink += strspn(weblink, " \t");
|
||||
|
||||
/* Cut out till next whitespace/newline */
|
||||
url = strndup(weblink, strcspn(weblink, WHITESPACE));
|
||||
if (!url)
|
||||
return log_oom();
|
||||
|
||||
if (!documentation_url_is_valid(url))
|
||||
goto notfound;
|
||||
|
||||
*ret = TAKE_PTR(url);
|
||||
return 1;
|
||||
|
||||
notfound:
|
||||
*ret = NULL;
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -128,9 +176,6 @@ static int field_set_test(const Set *fields, const char *name, size_t n) {
|
||||
return 1;
|
||||
|
||||
s = strndupa(name, n);
|
||||
if (!s)
|
||||
return log_oom();
|
||||
|
||||
return set_contains(fields, s);
|
||||
}
|
||||
|
||||
@@ -374,10 +419,13 @@ static int output_short(
|
||||
|
||||
int r;
|
||||
const void *data;
|
||||
size_t length;
|
||||
size_t 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, *config_file = NULL, *unit = NULL, *user_unit = 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, unit_len = 0, user_unit_len = 0;
|
||||
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,
|
||||
*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,
|
||||
unit_len = 0, user_unit_len = 0, documentation_url_len = 0;
|
||||
int p = LOG_INFO;
|
||||
bool ellipsized = false, audit;
|
||||
const ParseFieldVec fields[] = {
|
||||
@@ -394,6 +442,7 @@ static int output_short(
|
||||
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),
|
||||
PARSE_FIELD_VEC_ENTRY("DOCUMENTATION=", &documentation_url, &documentation_url_len),
|
||||
};
|
||||
size_t highlight_shifted[] = {highlight ? highlight[0] : 0, highlight ? highlight[1] : 0};
|
||||
|
||||
@@ -482,11 +531,42 @@ static int output_short(
|
||||
n += fake_pid_len + 2;
|
||||
}
|
||||
|
||||
fputs(": ", f);
|
||||
|
||||
if (urlify_enabled()) {
|
||||
_cleanup_free_ char *c = NULL;
|
||||
|
||||
/* Insert a hyperlink to a documentation URL before the message. Note that we don't make the
|
||||
* whole message a hyperlink, since otherwise the whole screen might end up being just
|
||||
* hyperlinks. Moreover, we want to be able to highlight parts of the message (such as the
|
||||
* config file, see below) hence let's keep the documentation URL link separate. */
|
||||
|
||||
if (documentation_url && shall_print(documentation_url, documentation_url_len, flags)) {
|
||||
c = strndup(documentation_url, documentation_url_len);
|
||||
if (!c)
|
||||
return log_oom();
|
||||
|
||||
if (!documentation_url_is_valid(c)) /* Eat up invalid links */
|
||||
c = mfree(c);
|
||||
}
|
||||
|
||||
if (!c)
|
||||
(void) url_from_catalog(j, &c); /* Acquire from catalog if not embedded in log message itself */
|
||||
|
||||
if (c) {
|
||||
_cleanup_free_ char *urlified = NULL;
|
||||
|
||||
if (terminal_urlify(c, special_glyph(SPECIAL_GLYPH_EXTERNAL_LINK), &urlified) >= 0) {
|
||||
fputs(urlified, f);
|
||||
fputc(' ', f);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!(flags & OUTPUT_SHOW_ALL) && !utf8_is_printable(message, message_len)) {
|
||||
char bytes[FORMAT_BYTES_MAX];
|
||||
fprintf(f, ": [%s blob data]\n", format_bytes(bytes, sizeof(bytes), message_len));
|
||||
fprintf(f, "[%s blob data]\n", format_bytes(bytes, sizeof(bytes), message_len));
|
||||
} else {
|
||||
fputs(": ", f);
|
||||
|
||||
/* URLify config_file string in message, if the message starts with it.
|
||||
* Skip URLification if the highlighted pattern overlaps. */
|
||||
@@ -522,7 +602,7 @@ static int output_short(
|
||||
}
|
||||
|
||||
if (flags & OUTPUT_CATALOG)
|
||||
print_catalog(f, j);
|
||||
(void) print_catalog(f, j);
|
||||
|
||||
return ellipsized;
|
||||
}
|
||||
@@ -641,7 +721,7 @@ static int output_verbose(
|
||||
return r;
|
||||
|
||||
if (flags & OUTPUT_CATALOG)
|
||||
print_catalog(f, j);
|
||||
(void) print_catalog(f, j);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -18,7 +18,7 @@
|
||||
#include "terminal-util.h"
|
||||
#include "util.h"
|
||||
|
||||
static bool urlify_enabled(void) {
|
||||
bool urlify_enabled(void) {
|
||||
static int cached_urlify_enabled = -1;
|
||||
|
||||
/* Unfortunately 'less' doesn't support links like this yet 😭, hence let's disable this as long as there's a
|
||||
|
||||
@@ -5,6 +5,8 @@ void print_separator(void);
|
||||
|
||||
int file_url_from_path(const char *path, char **ret);
|
||||
|
||||
bool urlify_enabled(void);
|
||||
|
||||
int terminal_urlify(const char *url, const char *text, char **ret);
|
||||
int terminal_urlify_path(const char *path, const char *text, char **ret);
|
||||
int terminal_urlify_man(const char *page, const char *section, char **ret);
|
||||
|
||||
@@ -107,6 +107,7 @@ static void dump_special_glyphs(void) {
|
||||
dump_glyph(SPECIAL_GLYPH_MU);
|
||||
dump_glyph(SPECIAL_GLYPH_CHECK_MARK);
|
||||
dump_glyph(SPECIAL_GLYPH_CROSS_MARK);
|
||||
dump_glyph(SPECIAL_GLYPH_EXTERNAL_LINK);
|
||||
dump_glyph(SPECIAL_GLYPH_ECSTATIC_SMILEY);
|
||||
dump_glyph(SPECIAL_GLYPH_HAPPY_SMILEY);
|
||||
dump_glyph(SPECIAL_GLYPH_SLIGHTLY_HAPPY_SMILEY);
|
||||
|
||||
Reference in New Issue
Block a user