mirror of
https://github.com/tstack/lnav.git
synced 2024-09-11 13:05:51 +03:00
parent
c3abaee445
commit
a324bc8c1c
@ -258,6 +258,11 @@ object with the following fields:
|
||||
of exact numbers, you can supply a pattern and the number found in the log
|
||||
will be converted to a string for pattern-matching.
|
||||
|
||||
.. note:: The regular expression is not anchored to the start of the
|
||||
string by default, so an expression like :code:`1` will match
|
||||
:code:`-1`. If you want to exactly match :code:`1`, you would
|
||||
use :code:`^1$` as the expression.
|
||||
|
||||
:multiline: If false, **lnav** will consider any log lines that do not
|
||||
match one of the message patterns to be in error when checking files with
|
||||
the '-C' option. This flag will not affect normal viewing operation.
|
||||
|
@ -390,6 +390,14 @@ code_border(S str)
|
||||
VC_ROLE.template value(role_t::VCR_CODE_BORDER));
|
||||
}
|
||||
|
||||
template<typename S>
|
||||
inline std::pair<S, string_attr_pair>
|
||||
snippet_border(S str)
|
||||
{
|
||||
return std::make_pair(std::move(str),
|
||||
VC_ROLE.template value(role_t::VCR_SNIPPET_BORDER));
|
||||
}
|
||||
|
||||
template<typename S>
|
||||
inline std::pair<S, string_attr_pair>
|
||||
table_border(S str)
|
||||
|
@ -2281,19 +2281,75 @@ external_log_format::build(std::vector<lnav::console::user_message>& errors)
|
||||
.append("\n")
|
||||
.append("captured level = ")
|
||||
.append_quoted(level_cap->to_string());
|
||||
errors.emplace_back(
|
||||
lnav::console::user_message::error(
|
||||
attr_line_t("invalid sample log message: ")
|
||||
.append(lnav::to_json(elf_sample.s_line.pp_value)))
|
||||
.with_reason(attr_line_t()
|
||||
.append_quoted(lnav::roles::symbol(
|
||||
level_names[level]))
|
||||
.append(" does not match the expected "
|
||||
"level of ")
|
||||
.append_quoted(lnav::roles::symbol(
|
||||
level_names[elf_sample.s_level])))
|
||||
.with_snippet(elf_sample.s_line.to_snippet())
|
||||
.with_note(note_al));
|
||||
if (level_cap && !this->elf_level_patterns.empty()) {
|
||||
static thread_local auto md
|
||||
= lnav::pcre2pp::match_data::unitialized();
|
||||
|
||||
note_al.append("\nlevel regular expression match results:");
|
||||
for (const auto& level_pattern : this->elf_level_patterns) {
|
||||
attr_line_t regex_al = level_pattern.second.lp_pcre
|
||||
.pp_value->get_pattern();
|
||||
lnav::snippets::regex_highlighter(
|
||||
regex_al,
|
||||
-1,
|
||||
line_range{0, (int) regex_al.length()});
|
||||
note_al.append("\n ")
|
||||
.append(
|
||||
lnav::roles::symbol(level_pattern.second.lp_pcre
|
||||
.pp_path.to_string()))
|
||||
.append(" = ")
|
||||
.append(regex_al)
|
||||
.append("\n ");
|
||||
auto match_res = level_pattern.second.lp_pcre.pp_value
|
||||
->capture_from(level_cap.value())
|
||||
.into(md)
|
||||
.matches(PCRE2_NO_UTF_CHECK)
|
||||
.ignore_error();
|
||||
if (!match_res) {
|
||||
note_al.append(lnav::roles::warning("no match"));
|
||||
continue;
|
||||
}
|
||||
|
||||
note_al.append(level_cap.value())
|
||||
.append("\n ")
|
||||
.append(md.leading().length(), ' ')
|
||||
.append("^"_snippet_border);
|
||||
if (match_res->f_all.length() > 2) {
|
||||
note_al.append(
|
||||
lnav::roles::snippet_border(std::string(
|
||||
match_res->f_all.length() - 2, '-')));
|
||||
}
|
||||
if (match_res->f_all.length() > 1) {
|
||||
note_al.append("^"_snippet_border);
|
||||
}
|
||||
}
|
||||
}
|
||||
auto um
|
||||
= lnav::console::user_message::error(
|
||||
attr_line_t("invalid sample log message: ")
|
||||
.append(
|
||||
lnav::to_json(elf_sample.s_line.pp_value)))
|
||||
.with_reason(
|
||||
attr_line_t()
|
||||
.append_quoted(
|
||||
lnav::roles::symbol(level_names[level]))
|
||||
.append(" does not match the expected "
|
||||
"level of ")
|
||||
.append_quoted(lnav::roles::symbol(
|
||||
level_names[elf_sample.s_level])))
|
||||
.with_snippet(elf_sample.s_line.to_snippet())
|
||||
.with_note(note_al);
|
||||
if (!this->elf_level_patterns.empty()) {
|
||||
um.with_help(
|
||||
attr_line_t("Level regexes are not anchored to the "
|
||||
"start/end of the string. Prepend ")
|
||||
.append_quoted("^"_symbol)
|
||||
.append(" to the expression to match from the "
|
||||
"start of the string and append ")
|
||||
.append_quoted("$"_symbol)
|
||||
.append(" to match up to the end of the string."));
|
||||
}
|
||||
errors.emplace_back(um);
|
||||
}
|
||||
|
||||
{
|
||||
|
@ -300,6 +300,7 @@ read_levels(yajlpp_parse_context* ypc, const unsigned char* str, size_t len)
|
||||
log_level_t level = string2level(level_name_or_number.c_str());
|
||||
auto value_frag = string_fragment::from_bytes(str, len);
|
||||
|
||||
elf->elf_level_patterns[level].lp_pcre.pp_path = ypc->get_full_path();
|
||||
auto compile_res = lnav::pcre2pp::code::from(value_frag);
|
||||
if (compile_res.isErr()) {
|
||||
static const intern_string_t PATTERN_SRC
|
||||
|
@ -25,6 +25,10 @@
|
||||
}
|
||||
},
|
||||
"level-field": "level",
|
||||
"level": {
|
||||
"info": "info",
|
||||
"debug": "debug"
|
||||
},
|
||||
"sample": [
|
||||
{
|
||||
"line": "abc: foo"
|
||||
|
@ -116,24 +116,31 @@
|
||||
[36m =[0m [36mhelp[0m: update the regular expression to fully capture the sample message
|
||||
[1m[31m✘ error[0m: invalid sample log message: "abc: foo"
|
||||
[1m[31mreason[0m: unrecognized timestamp -- abc
|
||||
[36m --> [0m[1m{test_dir}/bad-config/formats/invalid-sample/format.json[0m:30
|
||||
[36m --> [0m[1m{test_dir}/bad-config/formats/invalid-sample/format.json[0m:34
|
||||
[36m =[0m [36mnote[0m: the following custom formats were tried:
|
||||
abc
|
||||
[36m^ [0m“[1m%i[0m”[36m matched up to here[0m
|
||||
[36m =[0m [36mhelp[0m: If the timestamp format is not supported by default, you can add a custom format with the “[1mtimestamp-format[0m” property
|
||||
[1m[31m✘ error[0m: invalid sample log message: "1428634687123| debug hello"
|
||||
[1m[31mreason[0m: “[1mdebug[0m” does not match the expected level of “[1minfo[0m”
|
||||
[36m --> [0m[1m{test_dir}/bad-config/formats/invalid-sample/format.json[0m:33
|
||||
[36m --> [0m[1m{test_dir}/bad-config/formats/invalid-sample/format.json[0m:37
|
||||
[36m =[0m [36mnote[0m: matched regex = [1mwith-level[0m
|
||||
captured level = “debug”
|
||||
level regular expression match results:
|
||||
[1m/bad_sample_log/level/debug[0m = debug
|
||||
debug
|
||||
[36m^[0m[36m---[0m[36m^[0m
|
||||
[1m/bad_sample_log/level/info[0m = info
|
||||
[33mno match[0m
|
||||
[36m =[0m [36mhelp[0m: Level regexes are not anchored to the start/end of the string. Prepend “[1m^[0m” to the expression to match from the start of the string and append “[1m$[0m” to match up to the end of the string.
|
||||
[1m[31m✘ error[0m: invalid pattern: “[1mwith-level[0m”
|
||||
[1m[31mreason[0m: pattern does not match entire multiline sample message
|
||||
[36m --> [0m[1m{test_dir}/bad-config/formats/invalid-sample/format.json[0m:37
|
||||
[36m --> [0m[1m{test_dir}/bad-config/formats/invalid-sample/format.json[0m:41
|
||||
[36m =[0m [36mnote[0m: [1mwith-level[0m = [1m[36m^[0m[1m[32m([0m[1m[32m?[0m[1m[36m<[0mtimestamp>[1m\d[0m[1m[36m+[0m[1m[32m)[0m\| [1m[32m([0m[1m[32m?[0m[1m[36m<[0mlevel>[1m\w[0m[1m[36m+[0m[1m[32m)[0m [1m[32m([0m[1m[32m?[0m[1m[36m<[0mbody>[1m\w[0m[1m[36m+[0m[1m[32m)[0m[1m[36m$[0m
|
||||
[36m =[0m [36mhelp[0m: use “.*” to match new-lines
|
||||
[1m[31m✘ error[0m: invalid sample log message: "1428634687123; foo bar"
|
||||
[1m[31mreason[0m: sample does not match any patterns
|
||||
[36m --> [0m[1m{test_dir}/bad-config/formats/invalid-sample/format.json[0m:41
|
||||
[36m --> [0m[1m{test_dir}/bad-config/formats/invalid-sample/format.json[0m:45
|
||||
[36m =[0m [36mnote[0m: the following shows how each pattern matched this sample:
|
||||
[37m[40m1428634687123; foo bar[0m
|
||||
[36m^ [0m[1mbad-time[0m[36m matched up to here[0m
|
||||
|
Loading…
Reference in New Issue
Block a user