mirror of
https://github.com/systemd/systemd-stable.git
synced 2025-01-08 21:17:47 +03:00
journalctl: add highlighting for matched substring
Red is used for highligting, the same as grep does. Except when the line is highlighted red already, because it has high priority, in which case plain ansi highlight is used for the matched substring. Coloring is implemented for short and cat outputs, and not for other types. I guess we could also add it for verbose output in the future.
This commit is contained in:
parent
61c5f8a1f0
commit
b4766d5f15
@ -30,6 +30,7 @@
|
||||
#include "gunicode.h"
|
||||
#include "macro.h"
|
||||
#include "string-util.h"
|
||||
#include "terminal-util.h"
|
||||
#include "utf8.h"
|
||||
#include "util.h"
|
||||
|
||||
@ -648,7 +649,17 @@ char *strreplace(const char *text, const char *old_string, const char *new_strin
|
||||
return ret;
|
||||
}
|
||||
|
||||
char *strip_tab_ansi(char **ibuf, size_t *_isz) {
|
||||
static void advance_offsets(ssize_t diff, size_t offsets[2], size_t shift[2], size_t size) {
|
||||
if (!offsets)
|
||||
return;
|
||||
|
||||
if ((size_t) diff < offsets[0])
|
||||
shift[0] += size;
|
||||
if ((size_t) diff < offsets[1])
|
||||
shift[1] += size;
|
||||
}
|
||||
|
||||
char *strip_tab_ansi(char **ibuf, size_t *_isz, size_t highlight[2]) {
|
||||
const char *i, *begin = NULL;
|
||||
enum {
|
||||
STATE_OTHER,
|
||||
@ -656,7 +667,7 @@ char *strip_tab_ansi(char **ibuf, size_t *_isz) {
|
||||
STATE_BRACKET
|
||||
} state = STATE_OTHER;
|
||||
char *obuf = NULL;
|
||||
size_t osz = 0, isz;
|
||||
size_t osz = 0, isz, shift[2] = {};
|
||||
FILE *f;
|
||||
|
||||
assert(ibuf);
|
||||
@ -684,15 +695,18 @@ char *strip_tab_ansi(char **ibuf, size_t *_isz) {
|
||||
break;
|
||||
else if (*i == '\x1B')
|
||||
state = STATE_ESCAPE;
|
||||
else if (*i == '\t')
|
||||
else if (*i == '\t') {
|
||||
fputs(" ", f);
|
||||
else
|
||||
advance_offsets(i - *ibuf, highlight, shift, 7);
|
||||
} else
|
||||
fputc(*i, f);
|
||||
|
||||
break;
|
||||
|
||||
case STATE_ESCAPE:
|
||||
if (i >= *ibuf + isz) { /* EOT */
|
||||
fputc('\x1B', f);
|
||||
advance_offsets(i - *ibuf, highlight, shift, 1);
|
||||
break;
|
||||
} else if (*i == '[') {
|
||||
state = STATE_BRACKET;
|
||||
@ -700,6 +714,7 @@ char *strip_tab_ansi(char **ibuf, size_t *_isz) {
|
||||
} else {
|
||||
fputc('\x1B', f);
|
||||
fputc(*i, f);
|
||||
advance_offsets(i - *ibuf, highlight, shift, 1);
|
||||
state = STATE_OTHER;
|
||||
}
|
||||
|
||||
@ -711,6 +726,7 @@ char *strip_tab_ansi(char **ibuf, size_t *_isz) {
|
||||
(!(*i >= '0' && *i <= '9') && !IN_SET(*i, ';', 'm'))) {
|
||||
fputc('\x1B', f);
|
||||
fputc('[', f);
|
||||
advance_offsets(i - *ibuf, highlight, shift, 2);
|
||||
state = STATE_OTHER;
|
||||
i = begin-1;
|
||||
} else if (*i == 'm')
|
||||
@ -732,6 +748,11 @@ char *strip_tab_ansi(char **ibuf, size_t *_isz) {
|
||||
if (_isz)
|
||||
*_isz = osz;
|
||||
|
||||
if (highlight) {
|
||||
highlight[0] += shift[0];
|
||||
highlight[1] += shift[1];
|
||||
}
|
||||
|
||||
return obuf;
|
||||
}
|
||||
|
||||
|
@ -177,7 +177,7 @@ char* strshorten(char *s, size_t l);
|
||||
|
||||
char *strreplace(const char *text, const char *old_string, const char *new_string);
|
||||
|
||||
char *strip_tab_ansi(char **p, size_t *l);
|
||||
char *strip_tab_ansi(char **ibuf, size_t *_isz, size_t highlight[2]);
|
||||
|
||||
char *strextend_with_separator(char **x, const char *separator, ...) _sentinel_;
|
||||
|
||||
|
@ -226,7 +226,8 @@ static ssize_t request_reader_entries(
|
||||
return MHD_CONTENT_READER_END_WITH_ERROR;
|
||||
}
|
||||
|
||||
r = output_journal(m->tmp, m->journal, m->mode, 0, OUTPUT_FULL_WIDTH, NULL, NULL);
|
||||
r = output_journal(m->tmp, m->journal, m->mode, 0, OUTPUT_FULL_WIDTH,
|
||||
NULL, NULL, NULL);
|
||||
if (r < 0) {
|
||||
log_error_errno(r, "Failed to serialize item: %m");
|
||||
return MHD_CONTENT_READER_END_WITH_ERROR;
|
||||
|
@ -2516,6 +2516,7 @@ int main(int argc, char *argv[]) {
|
||||
for (;;) {
|
||||
while (arg_lines < 0 || n_shown < arg_lines || (arg_follow && !first_line)) {
|
||||
int flags;
|
||||
size_t highlight[2] = {};
|
||||
|
||||
if (need_seek) {
|
||||
if (!arg_reverse)
|
||||
@ -2574,6 +2575,7 @@ int main(int argc, char *argv[]) {
|
||||
_cleanup_(pcre2_match_data_freep) pcre2_match_data *md = NULL;
|
||||
const void *message;
|
||||
size_t len;
|
||||
PCRE2_SIZE *ovec;
|
||||
|
||||
md = pcre2_match_data_create(1, NULL);
|
||||
if (!md)
|
||||
@ -2613,6 +2615,10 @@ int main(int argc, char *argv[]) {
|
||||
r = -EINVAL;
|
||||
goto finish;
|
||||
}
|
||||
|
||||
ovec = pcre2_get_ovector_pointer(md);
|
||||
highlight[0] = ovec[0];
|
||||
highlight[1] = ovec[1];
|
||||
}
|
||||
#endif
|
||||
|
||||
@ -2624,7 +2630,8 @@ int main(int argc, char *argv[]) {
|
||||
arg_utc * OUTPUT_UTC |
|
||||
arg_no_hostname * OUTPUT_NO_HOSTNAME;
|
||||
|
||||
r = output_journal(stdout, j, arg_output, 0, flags, arg_output_fields, &ellipsized);
|
||||
r = output_journal(stdout, j, arg_output, 0, flags,
|
||||
arg_output_fields, highlight, &ellipsized);
|
||||
need_seek = true;
|
||||
if (r == -EADDRNOTAVAIL)
|
||||
break;
|
||||
|
@ -166,8 +166,17 @@ static bool shall_print(const char *p, size_t l, OutputFlags flags) {
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool print_multiline(FILE *f, unsigned prefix, unsigned n_columns, OutputFlags flags, int priority, const char* message, size_t message_len) {
|
||||
const char *color_on = "", *color_off = "";
|
||||
static bool print_multiline(
|
||||
FILE *f,
|
||||
unsigned prefix,
|
||||
unsigned n_columns,
|
||||
OutputFlags flags,
|
||||
int priority,
|
||||
const char* message,
|
||||
size_t message_len,
|
||||
size_t highlight[2]) {
|
||||
|
||||
const char *color_on = "", *color_off = "", *highlight_on = "";
|
||||
const char *pos, *end;
|
||||
bool ellipsized = false;
|
||||
int line = 0;
|
||||
@ -176,9 +185,11 @@ static bool print_multiline(FILE *f, unsigned prefix, unsigned n_columns, Output
|
||||
if (priority <= LOG_ERR) {
|
||||
color_on = ANSI_HIGHLIGHT_RED;
|
||||
color_off = ANSI_NORMAL;
|
||||
highlight_on = ANSI_HIGHLIGHT;
|
||||
} else if (priority <= LOG_NOTICE) {
|
||||
color_on = ANSI_HIGHLIGHT;
|
||||
color_off = ANSI_NORMAL;
|
||||
highlight_on = ANSI_HIGHLIGHT_RED;
|
||||
}
|
||||
}
|
||||
|
||||
@ -209,9 +220,28 @@ static bool print_multiline(FILE *f, unsigned prefix, unsigned n_columns, Output
|
||||
|
||||
if (flags & (OUTPUT_FULL_WIDTH | OUTPUT_SHOW_ALL) ||
|
||||
(prefix + len + 1 < n_columns && !tail_line)) {
|
||||
fprintf(f, "%*s%s%.*s%s\n",
|
||||
continuation * prefix, "",
|
||||
color_on, len, pos, color_off);
|
||||
if (highlight &&
|
||||
(size_t) (pos - message) <= highlight[0] &&
|
||||
highlight[0] < (size_t) len) {
|
||||
|
||||
fprintf(f, "%*s%s%.*s",
|
||||
continuation * prefix, "",
|
||||
color_on, (int) highlight[0], pos);
|
||||
fprintf(f, "%s%.*s",
|
||||
highlight_on,
|
||||
(int) (MIN((size_t) len, highlight[1]) - highlight[0]),
|
||||
pos + highlight[0]);
|
||||
if ((size_t) len > highlight[1])
|
||||
fprintf(f, "%s%.*s",
|
||||
color_on,
|
||||
(int) (len - highlight[1]),
|
||||
pos + highlight[1]);
|
||||
fprintf(f, "%s\n", color_off);
|
||||
|
||||
} else
|
||||
fprintf(f, "%*s%s%.*s%s\n",
|
||||
continuation * prefix, "",
|
||||
color_on, len, pos, color_off);
|
||||
continue;
|
||||
}
|
||||
|
||||
@ -369,7 +399,8 @@ static int output_short(
|
||||
OutputMode mode,
|
||||
unsigned n_columns,
|
||||
OutputFlags flags,
|
||||
Set *output_fields) {
|
||||
Set *output_fields,
|
||||
size_t highlight[2]) {
|
||||
|
||||
int r;
|
||||
const void *data;
|
||||
@ -390,6 +421,7 @@ static int output_short(
|
||||
PARSE_FIELD_VEC_ENTRY("_SOURCE_REALTIME_TIMESTAMP=", &realtime, &realtime_len),
|
||||
PARSE_FIELD_VEC_ENTRY("_SOURCE_MONOTONIC_TIMESTAMP=", &monotonic, &monotonic_len),
|
||||
};
|
||||
size_t highlight_shifted[] = {highlight ? highlight[0] : 0, highlight ? highlight[1] : 0};
|
||||
|
||||
assert(f);
|
||||
assert(j);
|
||||
@ -421,7 +453,7 @@ static int output_short(
|
||||
}
|
||||
|
||||
if (!(flags & OUTPUT_SHOW_ALL))
|
||||
strip_tab_ansi(&message, &message_len);
|
||||
strip_tab_ansi(&message, &message_len, highlight_shifted);
|
||||
|
||||
if (priority_len == 1 && *priority >= '0' && *priority <= '7')
|
||||
p = *priority - '0';
|
||||
@ -468,7 +500,9 @@ static int output_short(
|
||||
} else {
|
||||
fputs(": ", f);
|
||||
ellipsized |=
|
||||
print_multiline(f, n + 2, n_columns, flags, p, message, message_len);
|
||||
print_multiline(f, n + 2, n_columns, flags, p,
|
||||
message, message_len,
|
||||
highlight_shifted);
|
||||
}
|
||||
|
||||
if (flags & OUTPUT_CATALOG)
|
||||
@ -483,7 +517,8 @@ static int output_verbose(
|
||||
OutputMode mode,
|
||||
unsigned n_columns,
|
||||
OutputFlags flags,
|
||||
Set *output_fields) {
|
||||
Set *output_fields,
|
||||
size_t highlight[2]) {
|
||||
|
||||
const void *data;
|
||||
size_t length;
|
||||
@ -561,7 +596,7 @@ static int output_verbose(
|
||||
(((length < PRINT_CHAR_THRESHOLD) || flags & OUTPUT_FULL_WIDTH)
|
||||
&& utf8_is_printable(data, length))) {
|
||||
fprintf(f, " %s%.*s=", on, fieldlen, (const char*)data);
|
||||
print_multiline(f, 4 + fieldlen + 1, 0, OUTPUT_FULL_WIDTH, 0, c + 1, length - fieldlen - 1);
|
||||
print_multiline(f, 4 + fieldlen + 1, 0, OUTPUT_FULL_WIDTH, 0, c + 1, length - fieldlen - 1, NULL);
|
||||
fputs(off, f);
|
||||
} else {
|
||||
char bytes[FORMAT_BYTES_MAX];
|
||||
@ -590,7 +625,8 @@ static int output_export(
|
||||
OutputMode mode,
|
||||
unsigned n_columns,
|
||||
OutputFlags flags,
|
||||
Set *output_fields) {
|
||||
Set *output_fields,
|
||||
size_t highlight[2]) {
|
||||
|
||||
sd_id128_t boot_id;
|
||||
char sid[33];
|
||||
@ -728,7 +764,8 @@ static int output_json(
|
||||
OutputMode mode,
|
||||
unsigned n_columns,
|
||||
OutputFlags flags,
|
||||
Set *output_fields) {
|
||||
Set *output_fields,
|
||||
size_t highlight[2]) {
|
||||
|
||||
uint64_t realtime, monotonic;
|
||||
_cleanup_free_ char *cursor = NULL;
|
||||
@ -952,15 +989,22 @@ static int output_cat(
|
||||
OutputMode mode,
|
||||
unsigned n_columns,
|
||||
OutputFlags flags,
|
||||
Set *output_fields) {
|
||||
Set *output_fields,
|
||||
size_t highlight[2]) {
|
||||
|
||||
const void *data;
|
||||
size_t l;
|
||||
int r;
|
||||
const char *highlight_on = "", *highlight_off = "";
|
||||
|
||||
assert(j);
|
||||
assert(f);
|
||||
|
||||
if (flags & OUTPUT_COLOR) {
|
||||
highlight_on = ANSI_HIGHLIGHT_RED;
|
||||
highlight_off = ANSI_NORMAL;
|
||||
}
|
||||
|
||||
sd_journal_set_data_threshold(j, 0);
|
||||
|
||||
r = sd_journal_get_data(j, "MESSAGE", &data, &l);
|
||||
@ -974,7 +1018,17 @@ static int output_cat(
|
||||
|
||||
assert(l >= 8);
|
||||
|
||||
fwrite((const char*) data + 8, 1, l - 8, f);
|
||||
if (highlight && (flags & OUTPUT_COLOR)) {
|
||||
assert(highlight[0] <= highlight[1]);
|
||||
assert(highlight[1] <= l - 8);
|
||||
|
||||
fwrite((const char*) data + 8, 1, highlight[0], f);
|
||||
fwrite(highlight_on, 1, strlen(highlight_on), f);
|
||||
fwrite((const char*) data + 8 + highlight[0], 1, highlight[1] - highlight[0], f);
|
||||
fwrite(highlight_off, 1, strlen(highlight_off), f);
|
||||
fwrite((const char*) data + 8 + highlight[1], 1, l - 8 - highlight[1], f);
|
||||
} else
|
||||
fwrite((const char*) data + 8, 1, l - 8, f);
|
||||
fputc('\n', f);
|
||||
|
||||
return 0;
|
||||
@ -986,7 +1040,8 @@ static int (*output_funcs[_OUTPUT_MODE_MAX])(
|
||||
OutputMode mode,
|
||||
unsigned n_columns,
|
||||
OutputFlags flags,
|
||||
Set *output_fields) = {
|
||||
Set *output_fields,
|
||||
size_t highlight[2]) = {
|
||||
|
||||
[OUTPUT_SHORT] = output_short,
|
||||
[OUTPUT_SHORT_ISO] = output_short,
|
||||
@ -1010,6 +1065,7 @@ int output_journal(
|
||||
unsigned n_columns,
|
||||
OutputFlags flags,
|
||||
char **output_fields,
|
||||
size_t highlight[2],
|
||||
bool *ellipsized) {
|
||||
|
||||
int ret;
|
||||
@ -1030,7 +1086,7 @@ int output_journal(
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = output_funcs[mode](f, j, mode, n_columns, flags, fields);
|
||||
ret = output_funcs[mode](f, j, mode, n_columns, flags, fields, highlight);
|
||||
|
||||
if (ellipsized && ret > 0)
|
||||
*ellipsized = true;
|
||||
@ -1112,7 +1168,7 @@ static int show_journal(FILE *f,
|
||||
line++;
|
||||
maybe_print_begin_newline(f, &flags);
|
||||
|
||||
r = output_journal(f, j, mode, n_columns, flags, NULL, ellipsized);
|
||||
r = output_journal(f, j, mode, n_columns, flags, NULL, NULL, ellipsized);
|
||||
if (r < 0)
|
||||
return r;
|
||||
}
|
||||
|
@ -39,6 +39,7 @@ int output_journal(
|
||||
unsigned n_columns,
|
||||
OutputFlags flags,
|
||||
char **output_fields,
|
||||
size_t highlight[2],
|
||||
bool *ellipsized);
|
||||
|
||||
int add_match_this_boot(sd_journal *j, const char *machine);
|
||||
|
@ -28,24 +28,24 @@ int main(int argc, char *argv[]) {
|
||||
char *p;
|
||||
|
||||
assert_se(p = strdup("\tFoobar\tbar\twaldo\t"));
|
||||
assert_se(strip_tab_ansi(&p, NULL));
|
||||
assert_se(strip_tab_ansi(&p, NULL, NULL));
|
||||
fprintf(stdout, "<%s>\n", p);
|
||||
assert_se(streq(p, " Foobar bar waldo "));
|
||||
free(p);
|
||||
|
||||
assert_se(p = strdup(ANSI_HIGHLIGHT "Hello" ANSI_NORMAL ANSI_HIGHLIGHT_RED " world!" ANSI_NORMAL));
|
||||
assert_se(strip_tab_ansi(&p, NULL));
|
||||
assert_se(strip_tab_ansi(&p, NULL, NULL));
|
||||
fprintf(stdout, "<%s>\n", p);
|
||||
assert_se(streq(p, "Hello world!"));
|
||||
free(p);
|
||||
|
||||
assert_se(p = strdup("\x1B[\x1B[\t\x1B[" ANSI_HIGHLIGHT "\x1B[" "Hello" ANSI_NORMAL ANSI_HIGHLIGHT_RED " world!" ANSI_NORMAL));
|
||||
assert_se(strip_tab_ansi(&p, NULL));
|
||||
assert_se(strip_tab_ansi(&p, NULL, NULL));
|
||||
assert_se(streq(p, "\x1B[\x1B[ \x1B[\x1B[Hello world!"));
|
||||
free(p);
|
||||
|
||||
assert_se(p = strdup("\x1B[waldo"));
|
||||
assert_se(strip_tab_ansi(&p, NULL));
|
||||
assert_se(strip_tab_ansi(&p, NULL, NULL));
|
||||
assert_se(streq(p, "\x1B[waldo"));
|
||||
free(p);
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user