[partitions] revive partition functionality

pull/1242/head
Tim Stack 2 months ago
parent 5f28edd1c1
commit 554f0e2439

@ -1,5 +1,16 @@
## lnav v0.12.1
Features:
* Log partitions can automatically be created by defining a log
message pattern in a log format. Under a format definition,
add an entry into the "partitions" object in a format definition.
The "pattern" property specifies the regular expression to match
against a line in a file that matches the format. If a match is
found, the partition name will be set to the value(s) captured
by the regex. To restrict matches to certain files, you can add
a "paths" array whose object elements contain a "glob" property
that will be matched against file names.
Interface changes:
* Changed the breadcrumb bar styling to space things out
more and make the divisions between items clearer.
@ -14,6 +25,14 @@ Interface changes:
one is active).
* The focused line should be preserved more reliably in
the LOG/TEXT views.
* In the LOG view, the current partition name (as set
with the `:partition-name` command) is shown as the
first breadcrumb in the breadcrumb bar. And, when
that breadcrumb is selected, you can select another
partition to jump to.
* The `{` / `}` hotkeys, `:next-section`, and `:prev-section`
commands now work in the LOG view and take you to the
next/previous partition.
## lnav v0.12.0

@ -518,6 +518,74 @@
},
"additionalProperties": false
},
"partitions": {
"description": "The partitions to automatically apply to log messages",
"title": "/<format_name>/partitions",
"type": "object",
"patternProperties": {
"([\\w:;\\._\\-]+)": {
"description": "The type of partition to apply",
"title": "/<format_name>/partitions/<partition_type>",
"type": "object",
"properties": {
"paths": {
"description": "Restrict partitioning to the given paths",
"title": "/<format_name>/partitions/<partition_type>/paths",
"type": "array",
"items": {
"type": "object",
"properties": {
"glob": {
"title": "/<format_name>/partitions/<partition_type>/paths/glob",
"description": "The glob to match against file paths",
"type": "string",
"examples": [
"*/system.log*"
]
}
},
"additionalProperties": false
}
},
"pattern": {
"title": "/<format_name>/partitions/<partition_type>/pattern",
"description": "The regular expression to match against the body of the log message",
"type": "string",
"examples": [
"\\w+ is down"
]
},
"description": {
"title": "/<format_name>/partitions/<partition_type>/description",
"description": "A description of this partition",
"type": "string"
},
"level": {
"title": "/<format_name>/partitions/<partition_type>/level",
"description": "Constrain hits to log messages with this level",
"type": "string",
"enum": [
"trace",
"debug5",
"debug4",
"debug3",
"debug2",
"debug",
"info",
"stats",
"notice",
"warning",
"error",
"critical",
"fatal"
]
}
},
"additionalProperties": false
}
},
"additionalProperties": false
},
"action": {
"title": "/<format_name>/action",
"type": "object",

@ -367,6 +367,19 @@ object with the following fields:
:glob: A glob pattern to check against the log files read by lnav.
:partitions: This object contains a description of partitions that should
automatically be created in the log view.
:pattern: The regular expression evaluated over a line in the log file as
it is read in. If there is a match, the log message the line is a part
of will be used as the start of the partition. The name of the
partition will be taken from any captures in the regex.
:paths: This array contains objects that define restrictions on the file
paths in which partitions will be created. The objects in this array
can contain:
:glob: A glob pattern to check against the log files read by lnav.
.. _format_sample:
:sample: A list of objects that contain sample log messages. All formats

@ -265,10 +265,41 @@ columns will be included in the table.
Taking Notes
------------
A few of the columns in the log tables can be updated on a row-by-row basis to
allow you to take notes. The majority of the columns in a log table are
read-only since they are backed by the log files themselves. However, the
following columns can be changed by an :code:`UPDATE` statement:
As you are looking through logs, you might find that you want to leave some
notes of your findings. **lnav** can help here by saving information in
the session without needing to modify the actual log files. Thus, when
you re-open the files in lnav, the notes will be restored. The following
types of information can be saved:
:tags: Log messages can be tagged with the :ref:`:tag<tag>` command as a
simple way to leave a descriptive mark. The tags attached to a
message will be shown underneath the message. You can press
:kbd:`u` and :kbd:`Shift` + :kbd:`u` to jump to the next/previous
marked line. A regular search will also match tags.
:comments: Free-form text can be attached to a log message with the
:ref:`:comment<comment>` command. The comment will be shown
underneath the message. If the text contains markdown syntax,
it will be rendered to the best of the terminal's ability.
You can press :kbd:`u` and :kbd:`Shift` + :kbd:`u` to jump to the
next/previous marked line. A regular search will also match the
comment text.
:partitions: The log view can be partitioned to provide some context
about where you are in a collection of logs. For example, in logs
for a test run, partitions could be created with the name for each
test. The current partition is shown in the breadcrumb bar and
prefixed by the "⊑" symbol. You can select the partition breadcrumb
to jump to another partition. Pressing :kbd:`{` and :kbd:`}` will
jump to the next/previous partition.
Accessing notes through the SQLite interface
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
The note taking functionality in lnav can also be accessed through the
log tables exposed through SQLite. The majority of the columns in a log
table are read-only since they are backed by the log files themselves.
However, the following columns can be changed by an :code:`UPDATE` statement:
* **log_part** - The "partition" the log message belongs to. This column can
also be changed by the :ref:`:partition-name<partition_name>` command.

@ -64,10 +64,19 @@ bookmark_metadata::remove_tag(const std::string& tag)
}
bool
bookmark_metadata::empty() const
bookmark_metadata::empty(bookmark_metadata::categories props) const
{
return this->bm_name.empty() && this->bm_comment.empty()
&& this->bm_tags.empty() && this->bm_annotations.la_pairs.empty();
switch (props) {
case categories::any:
return this->bm_name.empty() && this->bm_comment.empty()
&& this->bm_tags.empty()
&& this->bm_annotations.la_pairs.empty();
case categories::partition:
return this->bm_name.empty();
case categories::notes:
return this->bm_comment.empty() && this->bm_tags.empty()
&& this->bm_annotations.la_pairs.empty();
}
}
void

@ -47,6 +47,33 @@ struct logmsg_annotations {
struct bookmark_metadata {
static std::unordered_set<std::string> KNOWN_TAGS;
enum class categories : int {
any = 0,
partition = 0x01,
notes = 0x02,
};
bool has(categories props) const
{
if (props == categories::any) {
return true;
}
if (props == categories::partition && !this->bm_name.empty()) {
return true;
}
if (props == categories::notes
&& (!this->bm_comment.empty()
|| !this->bm_annotations.la_pairs.empty()
|| !this->bm_tags.empty()))
{
return true;
}
return false;
}
std::string bm_name;
std::string bm_comment;
logmsg_annotations bm_annotations;
@ -56,7 +83,7 @@ struct bookmark_metadata {
bool remove_tag(const std::string& tag);
bool empty() const;
bool empty(categories props) const;
void clear();
};

@ -67,8 +67,7 @@ breadcrumb_curses::do_update()
attr_line_t crumbs_line;
for (const auto& crumb : crumbs) {
auto accum_width = crumbs_line.column_width();
auto elem_width = utf8_string_length(crumb.c_display_value.get_string())
.template unwrap();
auto elem_width = crumb.c_display_value.column_width();
auto is_selected = this->bc_selected_crumb
&& (crumb_index == this->bc_selected_crumb.value());

@ -1,6 +1,7 @@
{
"$schema": "https://lnav.org/schemas/format-v1.schema.json",
"nextflow_log": {
"title": "Nextflow log format",
"description": "Format file for nextflow.io logs",
"url": [
"https://nextflow.io/docs/latest/cli.html#execution-logs"

@ -241,6 +241,17 @@
"pattern": "^Expected equality of these values:"
}
},
"partitions": {
"test-partition": {
"description": "Partition for gtest sections",
"paths": [
{
"glob": "*/test.log"
}
],
"pattern": "^\\[ RUN \\] ([^\\n]+)"
}
},
"sample": [
{
"line": "2021-05-24T20:31:05.671Z - last log rotation time, 2021-05-24T09:30:02.683Z - time the service was last started, Section for VMware ESX, pid=1000080910, version=7.0.3, build=0, option=DEBUG"

@ -219,7 +219,7 @@ that you can always use `q` to pop the top view off of the stack.
| o/O | Move forward/backward to the log message with a matching 'operation ID' (opid) field. |
| u/U | Move forward/backward through any user bookmarks you have added using the 'm' key. This hotkey will also jump to the start of any log partitions that have been created with the 'partition-name' command. |
| s/S | Move to the next/previous "slow down" in the log message rate. A slow down is detected by measuring how quickly the message rate has changed over the previous several messages. For example, if one message is logged every second for five seconds and then the last message arrives five seconds later, the last message will be highlighted as a slow down. |
| {/} | Move to the previous/next location in history. Whenever you jump to a new location in the view, the location will be added to the history. The history is not updated when using only the arrow keys. |
| {/} | Move to the previous/next section in the view. In the LOG view, this moves through partitions. In other views, it moves through sections of documents. |
### Chronological Navigation

@ -11,8 +11,8 @@
"keymap_def_pop_view": "Press ${ansi_bold}q${ansi_norm} to return to the previous view",
"keymap_def_zoom": "Press ${ansi_bold}z${ansi_norm}/${ansi_bold}Z${ansi_norm} to zoom in/out",
"keymap_def_clear": "Press ${ansi_bold}C${ansi_norm} to clear marked messages",
"keymap_def_prev_section": "Press ${ansi_bold}{${ansi_norm} to move to the previous section in history",
"keymap_def_next_section": "Press ${ansi_bold}}${ansi_norm} to move to the next section in history",
"keymap_def_prev_section": "Press ${ansi_bold}{${ansi_norm} to move to the previous section in the view",
"keymap_def_next_section": "Press ${ansi_bold}}${ansi_norm} to move to the next section in the view",
"keymap_def_next_mark": "Press ${ansi_bold}c${ansi_norm} to copy marked lines to the clipboard; press ${ansi_bold}C${ansi_norm} to clear marked lines",
"keymap_def_time_offset": "Press ${ansi_bold}s${ansi_norm}/${ansi_bold}S${ansi_norm} to move forward/backward through slow downs"
},

@ -3703,10 +3703,12 @@ com_clear_comment(exec_context& ec,
bookmark_metadata& line_meta = *(line_meta_opt.value());
line_meta.bm_comment.clear();
if (line_meta.empty()) {
lss.erase_bookmark_metadata(tc->get_selection());
if (line_meta.empty(bookmark_metadata::categories::notes)) {
tc->set_user_mark(
&textview_curses::BM_META, tc->get_selection(), false);
if (line_meta.empty(bookmark_metadata::categories::any)) {
lss.erase_bookmark_metadata(tc->get_selection());
}
}
lss.set_line_meta_changed();
@ -3786,7 +3788,7 @@ com_untag(exec_context& ec, std::string cmdline, std::vector<std::string>& args)
auto line_meta_opt = lss.find_bookmark_metadata(tc->get_selection());
if (line_meta_opt) {
bookmark_metadata& line_meta = *(line_meta_opt.value());
auto& line_meta = *(line_meta_opt.value());
for (size_t lpc = 1; lpc < args.size(); lpc++) {
std::string tag = args[lpc];
@ -3796,7 +3798,7 @@ com_untag(exec_context& ec, std::string cmdline, std::vector<std::string>& args)
}
line_meta.remove_tag(tag);
}
if (line_meta.empty()) {
if (line_meta.empty(bookmark_metadata::categories::notes)) {
tc->set_user_mark(
&textview_curses::BM_META, tc->get_selection(), false);
}
@ -3868,12 +3870,14 @@ com_delete_tags(exec_context& ec,
line_meta->remove_tag(tag);
}
if (line_meta->empty()) {
lss.erase_bookmark_metadata(*iter);
size_t off = distance(vbm.begin(), iter);
if (line_meta->empty(bookmark_metadata::categories::notes)) {
size_t off = std::distance(vbm.begin(), iter);
tc->set_user_mark(&textview_curses::BM_META, *iter, false);
iter = next(vbm.begin(), off);
if (line_meta->empty(bookmark_metadata::categories::any)) {
lss.erase_bookmark_metadata(*iter);
}
iter = std::next(vbm.begin(), off);
} else {
++iter;
}
@ -3906,7 +3910,7 @@ com_partition_name(exec_context& ec,
args[1] = trim(remaining_args(cmdline, args));
tc.set_user_mark(
&textview_curses::BM_META, tc.get_selection(), true);
&textview_curses::BM_PARTITION, tc.get_selection(), true);
auto& line_meta = lss.get_bookmark_metadata(tc.get_selection());
@ -3932,7 +3936,7 @@ com_clear_partition(exec_context& ec,
} else if (args.size() == 1) {
textview_curses& tc = lnav_data.ld_views[LNV_LOG];
logfile_sub_source& lss = lnav_data.ld_log_source;
auto& bv = tc.get_bookmarks()[&textview_curses::BM_META];
auto& bv = tc.get_bookmarks()[&textview_curses::BM_PARTITION];
nonstd::optional<vis_line_t> part_start;
if (binary_search(bv.begin(), bv.end(), tc.get_selection())) {
@ -3948,10 +3952,12 @@ com_clear_partition(exec_context& ec,
auto& line_meta = lss.get_bookmark_metadata(part_start.value());
line_meta.bm_name.clear();
if (line_meta.empty()) {
lss.erase_bookmark_metadata(part_start.value());
if (line_meta.empty(bookmark_metadata::categories::partition)) {
tc.set_user_mark(
&textview_curses::BM_META, part_start.value(), false);
&textview_curses::BM_PARTITION, part_start.value(), false);
if (line_meta.empty(bookmark_metadata::categories::any)) {
lss.erase_bookmark_metadata(part_start.value());
}
}
retval = "info: cleared partition name";

@ -4240,5 +4240,11 @@ format_tag_def::path_restriction::matches(const char* fn) const
return fnmatch(this->p_glob.c_str(), fn, 0) == 0;
}
bool
format_partition_def::path_restriction::matches(const char* fn) const
{
return fnmatch(this->p_glob.c_str(), fn, 0) == 0;
}
/* XXX */
#include "log_format_impls.cc"

@ -581,6 +581,9 @@ public:
std::map<const intern_string_t, std::shared_ptr<format_tag_def>>
lf_tag_defs;
std::map<const intern_string_t, std::shared_ptr<format_partition_def>>
lf_partition_defs;
struct opid_descriptor {
positioned_property<intern_string_t> od_field;
factory_container<lnav::pcre2pp::code> od_extractor;

@ -146,7 +146,7 @@ public:
uint8_t opid = 0)
: ll_offset(off), ll_has_ansi(false), ll_time(t), ll_millis(millis),
ll_opid(opid), ll_sub_offset(0), ll_valid_utf(1), ll_level(lev),
ll_module_id(mod), ll_expr_mark(0)
ll_module_id(mod), ll_meta_mark(0), ll_expr_mark(0)
{
memset(this->ll_schema, 0, sizeof(this->ll_schema));
}
@ -157,7 +157,8 @@ public:
uint8_t mod = 0,
uint8_t opid = 0)
: ll_offset(off), ll_has_ansi(false), ll_opid(opid), ll_sub_offset(0),
ll_valid_utf(1), ll_level(lev), ll_module_id(mod), ll_expr_mark(0)
ll_valid_utf(1), ll_level(lev), ll_module_id(mod), ll_meta_mark(0),
ll_expr_mark(0)
{
this->set_time(tv);
memset(this->ll_schema, 0, sizeof(this->ll_schema));
@ -226,6 +227,10 @@ public:
bool is_marked() const { return this->ll_level & LEVEL_MARK; }
void set_meta_mark(bool val) { this->ll_meta_mark = val; }
bool is_meta_marked() const { return this->ll_meta_mark; }
void set_expr_mark(bool val) { this->ll_expr_mark = val; }
bool is_expr_marked() const { return this->ll_expr_mark; }
@ -367,7 +372,8 @@ private:
unsigned int ll_sub_offset : 15;
unsigned int ll_valid_utf : 1;
uint8_t ll_level;
uint8_t ll_module_id : 7;
uint8_t ll_module_id : 6;
uint8_t ll_meta_mark : 1;
uint8_t ll_expr_mark : 1;
char ll_schema[2];
};
@ -389,4 +395,23 @@ struct format_tag_def {
log_level_t ftd_level{LEVEL_UNKNOWN};
};
struct format_partition_def {
explicit format_partition_def(std::string name) : fpd_name(std::move(name))
{
}
struct path_restriction {
std::string p_glob;
bool matches(const char* fn) const;
};
std::string fpd_name;
std::string fpd_description;
std::vector<path_restriction> fpd_paths;
factory_container<lnav::pcre2pp::code, int>::with_default_args<PCRE2_DOTALL>
fpd_pattern;
log_level_t fpd_level{LEVEL_UNKNOWN};
};
#endif

@ -171,6 +171,26 @@ format_tag_def_provider(const yajlpp_provider_context& ypc,
return retval.get();
}
static format_partition_def*
format_partition_def_provider(const yajlpp_provider_context& ypc,
external_log_format* elf)
{
const intern_string_t partition_name = ypc.get_substr_i(0);
auto iter = elf->lf_partition_defs.find(partition_name);
std::shared_ptr<format_partition_def> retval;
if (iter == elf->lf_partition_defs.end()) {
retval = std::make_shared<format_partition_def>(
partition_name.to_string());
elf->lf_partition_defs[partition_name] = retval;
} else {
retval = iter->second;
}
return retval.get();
}
static scaling_factor*
scaling_factor_provider(const yajlpp_provider_context& ypc,
external_log_format::value_def* value_def)
@ -759,6 +779,35 @@ static const struct json_path_container tag_handlers = {
.with_children(format_tag_def_handlers),
};
static const struct json_path_container format_partition_def_handlers = {
yajlpp::property_handler("paths#")
.with_description("Restrict partitioning to the given paths")
.for_field(&format_partition_def::fpd_paths)
.with_children(tag_path_handlers),
yajlpp::property_handler("pattern")
.with_synopsis("<regex>")
.with_description("The regular expression to match against the body of "
"the log message")
.with_example("\\w+ is down")
.for_field(&format_partition_def::fpd_pattern),
yajlpp::property_handler("description")
.with_synopsis("<string>")
.with_description("A description of this partition")
.for_field(&format_partition_def::fpd_description),
json_path_handler("level")
.with_synopsis("<log-level>")
.with_description("Constrain hits to log messages with this level")
.with_enum_values(LEVEL_ENUM)
.for_field(&format_partition_def::fpd_level),
};
static const struct json_path_container partition_handlers = {
yajlpp::pattern_property_handler(R"((?<partition_type>[\w:;\._\-]+))")
.with_description("The type of partition to apply")
.with_obj_provider(format_partition_def_provider)
.with_children(format_partition_def_handlers),
};
static const struct json_path_container highlight_handlers = {
yajlpp::pattern_property_handler(R"((?<highlight_name>[^/]+))")
.with_description("The definition of a highlight")
@ -1010,6 +1059,11 @@ const struct json_path_container format_handlers = {
.with_description("The tags to automatically apply to log messages")
.with_children(tag_handlers),
yajlpp::property_handler("partitions")
.with_description(
"The partitions to automatically apply to log messages")
.with_children(partition_handlers),
yajlpp::property_handler("action").with_children(action_handlers),
yajlpp::property_handler("sample#")
.with_description("An array of sample log messages to be tested "

@ -626,7 +626,7 @@ vt_column(sqlite3_vtab_cursor* cur, sqlite3_context* ctx, int col)
case VT_COL_PARTITION: {
auto& vb = vt->tc->get_bookmarks();
const auto& bv = vb[&textview_curses::BM_META];
const auto& bv = vb[&textview_curses::BM_PARTITION];
if (bv.empty()) {
sqlite3_result_null(ctx);
@ -1985,23 +1985,35 @@ vt_update(sqlite3_vtab* tab,
tmp_bm.bm_annotations = parse_res.unwrap();
}
auto& bv = vt->tc->get_bookmarks()[&textview_curses::BM_META];
bool has_meta = part_name != nullptr || log_comment != nullptr
|| log_tags.has_value() || log_annos.has_value();
auto& bv_meta = vt->tc->get_bookmarks()[&textview_curses::BM_META];
bool has_meta = log_comment != nullptr || log_tags.has_value()
|| log_annos.has_value();
if (binary_search(bv.begin(), bv.end(), vrowid) && !has_meta) {
if (std::binary_search(bv_meta.begin(), bv_meta.end(), vrowid)
&& !has_meta)
{
vt->tc->set_user_mark(&textview_curses::BM_META, vrowid, false);
vt->lss->erase_bookmark_metadata(vrowid);
vt->lss->set_line_meta_changed();
}
if (!has_meta && part_name == nullptr) {
vt->lss->erase_bookmark_metadata(vrowid);
}
if (part_name) {
auto& line_meta = vt->lss->get_bookmark_metadata(vrowid);
line_meta.bm_name = std::string((const char*) part_name);
vt->tc->set_user_mark(&textview_curses::BM_PARTITION, vrowid, true);
} else {
vt->tc->set_user_mark(
&textview_curses::BM_PARTITION, vrowid, false);
}
if (has_meta) {
auto& line_meta = vt->lss->get_bookmark_metadata(vrowid);
vt->tc->set_user_mark(&textview_curses::BM_META, vrowid, true);
if (part_name) {
line_meta.bm_name = std::string((const char*) part_name);
} else {
if (part_name == nullptr) {
line_meta.bm_name.clear();
}
if (log_comment) {

@ -433,6 +433,27 @@ logfile::process_prefix(shared_buffer_ref& sbr,
this->lf_applicable_taggers.emplace_back(td_pair.second);
}
for (auto& pd_pair : this->lf_format->lf_partition_defs) {
bool matches = pd_pair.second->fpd_paths.empty();
for (const auto& pr : pd_pair.second->fpd_paths) {
if (pr.matches(this->lf_filename.c_str())) {
matches = true;
break;
}
}
if (!matches) {
continue;
}
log_info(
"%s: found applicable partition definition "
"/%s/partitions/%s",
this->lf_filename.c_str(),
this->lf_format->get_name().get(),
pd_pair.second->fpd_name.c_str());
this->lf_applicable_partitioners.emplace_back(pd_pair.second);
}
/*
* We'll go ahead and assume that any previous lines were
* written out at the same time as the last one, so we need to
@ -844,33 +865,60 @@ logfile::rebuild_index(nonstd::optional<ui_clock::time_point> deadline)
}
#endif
if (this->lf_format) {
if (!this->lf_applicable_taggers.empty()) {
auto sf = sbr.to_string_fragment();
auto sf = sbr.to_string_fragment();
for (const auto& td : this->lf_applicable_taggers) {
auto curr_ll = this->end() - 1;
for (const auto& td : this->lf_applicable_taggers) {
auto curr_ll = this->end() - 1;
if (td->ftd_level != LEVEL_UNKNOWN
&& td->ftd_level != curr_ll->get_msg_level())
{
continue;
}
if (td->ftd_level != LEVEL_UNKNOWN
&& td->ftd_level != curr_ll->get_msg_level())
{
continue;
if (td->ftd_pattern.pp_value
->find_in(sf, PCRE2_NO_UTF_CHECK)
.ignore_error()
.has_value())
{
while (curr_ll->is_continued()) {
--curr_ll;
}
curr_ll->set_meta_mark(true);
auto line_number = static_cast<uint32_t>(
std::distance(this->begin(), curr_ll));
this->lf_bookmark_metadata[line_number].add_tag(
td->ftd_name);
}
}
if (td->ftd_pattern.pp_value
->find_in(sf, PCRE2_NO_UTF_CHECK)
.ignore_error()
.has_value())
{
while (curr_ll->is_continued()) {
--curr_ll;
}
curr_ll->set_mark(true);
auto line_number = static_cast<uint32_t>(
std::distance(this->begin(), curr_ll));
this->lf_bookmark_metadata[line_number].add_tag(
td->ftd_name);
for (const auto& pd : this->lf_applicable_partitioners) {
static thread_local auto part_md
= lnav::pcre2pp::match_data::unitialized();
auto curr_ll = this->end() - 1;
if (pd->fpd_level != LEVEL_UNKNOWN
&& pd->fpd_level != curr_ll->get_msg_level())
{
continue;
}
auto match_res = pd->fpd_pattern.pp_value->capture_from(sf)
.into(part_md)
.matches(PCRE2_NO_UTF_CHECK)
.ignore_error();
if (match_res) {
while (curr_ll->is_continued()) {
--curr_ll;
}
curr_ll->set_meta_mark(true);
auto line_number = static_cast<uint32_t>(
std::distance(this->begin(), curr_ll));
this->lf_bookmark_metadata[line_number].bm_name
= part_md.to_string();
}
}

@ -472,6 +472,8 @@ private:
robin_hood::unordered_map<uint32_t, bookmark_metadata> lf_bookmark_metadata;
std::vector<std::shared_ptr<format_tag_def>> lf_applicable_taggers;
std::vector<std::shared_ptr<format_partition_def>>
lf_applicable_partitioners;
std::map<std::string, metadata> lf_embedded_metadata;
size_t lf_file_options_generation{0};
nonstd::optional<std::pair<std::string, lnav::file_options>>

@ -214,10 +214,10 @@ struct filtered_logline_cmp {
nonstd::optional<vis_line_t>
logfile_sub_source::find_from_time(const struct timeval& start) const
{
auto lb = lower_bound(this->lss_filtered_index.begin(),
this->lss_filtered_index.end(),
start,
filtered_logline_cmp(*this));
auto lb = std::lower_bound(this->lss_filtered_index.begin(),
this->lss_filtered_index.end(),
start,
filtered_logline_cmp(*this));
if (lb != this->lss_filtered_index.end()) {
return vis_line_t(lb - this->lss_filtered_index.begin());
}
@ -604,20 +604,15 @@ logfile_sub_source::text_attrs_for_line(textview_curses& lv,
lr, SA_FORMAT.value(this->lss_token_file->get_format()->get_name()));
{
const auto& bv = lv.get_bookmarks()[&textview_curses::BM_META];
bookmark_vector<vis_line_t>::const_iterator bv_iter;
bv_iter = lower_bound(bv.begin(), bv.end(), vis_line_t(row + 1));
if (bv_iter != bv.begin()) {
--bv_iter;
auto line_meta_opt = this->find_bookmark_metadata(*bv_iter);
if (line_meta_opt && !line_meta_opt.value()->bm_name.empty()) {
lr.lr_start = 0;
lr.lr_end = -1;
value_out.emplace_back(
lr, logline::L_PARTITION.value(line_meta_opt.value()));
}
auto line_meta_context = this->get_bookmark_metadata_context(
vis_line_t(row + 1), bookmark_metadata::categories::partition);
if (line_meta_context.bmc_current_metadata) {
lr.lr_start = 0;
lr.lr_end = -1;
value_out.emplace_back(
lr,
logline::L_PARTITION.value(
line_meta_context.bmc_current_metadata.value()));
}
auto line_meta_opt = this->find_bookmark_metadata(vis_line_t(row));
@ -1021,7 +1016,7 @@ logfile_sub_source::rebuild_index(
content_line_t con_line(
ld->ld_file_index * MAX_LINES_PER_FILE + line_index);
if (lf_iter->is_marked()) {
if (lf_iter->is_meta_marked()) {
auto start_iter = lf_iter;
while (start_iter->is_continued()) {
--start_iter;
@ -1032,9 +1027,20 @@ logfile_sub_source::rebuild_index(
* MAX_LINES_PER_FILE
+ start_index);
this->lss_user_marks[&textview_curses::BM_META]
.insert_once(start_con_line);
lf_iter->set_mark(false);
auto& line_meta
= ld->get_file_ptr()
->get_bookmark_metadata()[start_index];
if (line_meta.has(bookmark_metadata::categories::notes))
{
this->lss_user_marks[&textview_curses::BM_META]
.insert_once(start_con_line);
}
if (line_meta.has(
bookmark_metadata::categories::partition))
{
this->lss_user_marks[&textview_curses::BM_PARTITION]
.insert_once(start_con_line);
}
}
this->lss_index.push_back(con_line);
}
@ -1088,7 +1094,7 @@ logfile_sub_source::rebuild_index(
content_line_t con_line(file_index * MAX_LINES_PER_FILE
+ line_index);
if (lf_iter->is_marked()) {
if (lf_iter->is_meta_marked()) {
auto start_iter = lf_iter;
while (start_iter->is_continued()) {
--start_iter;
@ -1098,9 +1104,20 @@ logfile_sub_source::rebuild_index(
content_line_t start_con_line(
file_index * MAX_LINES_PER_FILE + start_index);
this->lss_user_marks[&textview_curses::BM_META]
.insert_once(start_con_line);
lf_iter->set_mark(false);
auto& line_meta
= ld->get_file_ptr()
->get_bookmark_metadata()[start_index];
if (line_meta.has(bookmark_metadata::categories::notes))
{
this->lss_user_marks[&textview_curses::BM_META]
.insert_once(start_con_line);
}
if (line_meta.has(
bookmark_metadata::categories::partition))
{
this->lss_user_marks[&textview_curses::BM_PARTITION]
.insert_once(start_con_line);
}
}
this->lss_index.push_back(con_line);
}
@ -2347,7 +2364,43 @@ logfile_sub_source::text_crumbs_for_line(int line,
return;
}
auto line_pair_opt = this->find_line_with_file(vis_line_t(line));
auto vl = vis_line_t(line);
auto bmc = this->get_bookmark_metadata_context(
vl, bookmark_metadata::categories::partition);
if (bmc.bmc_current_metadata) {
const auto& name = bmc.bmc_current_metadata.value()->bm_name;
auto key = text_anchors::to_anchor_string(name);
auto display = attr_line_t()
.append("\u2291 "_symbol)
.append(lnav::roles::variable(name));
crumbs.emplace_back(
key,
display,
[this]() -> std::vector<breadcrumb::possibility> {
auto& vb = this->tss_view->get_bookmarks();
const auto& bv = vb[&textview_curses::BM_PARTITION];
std::vector<breadcrumb::possibility> retval;
for (const auto& vl : bv) {
auto meta_opt = this->find_bookmark_metadata(vl);
if (!meta_opt || meta_opt.value()->bm_name.empty()) {
continue;
}
const auto& name = meta_opt.value()->bm_name;
retval.emplace_back(text_anchors::to_anchor_string(name),
name);
}
return retval;
},
[ec = this->lss_exec_context](const auto& part) {
ec->execute(fmt::format(FMT_STRING(":goto {}"),
part.template get<std::string>()));
});
}
auto line_pair_opt = this->find_line_with_file(vl);
if (!line_pair_opt) {
return;
}
@ -2627,8 +2680,62 @@ logfile_sub_source::get_bookmark_metadata(content_line_t cl)
return line_pair.first->get_bookmark_metadata()[line_number];
}
logfile_sub_source::bookmark_metadata_context
logfile_sub_source::get_bookmark_metadata_context(
vis_line_t vl, bookmark_metadata::categories desired) const
{
const auto& vb = this->tss_view->get_bookmarks();
const auto bv_iter
= vb.find(desired == bookmark_metadata::categories::partition
? &textview_curses::BM_PARTITION
: &textview_curses::BM_META);
if (bv_iter == vb.end()) {
return bookmark_metadata_context{};
}
const auto& bv = bv_iter->second;
auto vl_iter = std::lower_bound(bv.begin(), bv.end(), vl + 1_vl);
nonstd::optional<vis_line_t> next_line;
for (auto next_vl_iter = vl_iter; next_vl_iter != bv.end(); ++next_vl_iter)
{
auto bm_opt = this->find_bookmark_metadata(*next_vl_iter);
if (!bm_opt) {
continue;
}
if (bm_opt.value()->has(desired)) {
next_line = *next_vl_iter;
break;
}
}
if (vl_iter == bv.begin()) {
return bookmark_metadata_context{
nonstd::nullopt, nonstd::nullopt, next_line};
}
--vl_iter;
while (true) {
auto bm_opt = this->find_bookmark_metadata(*vl_iter);
if (bm_opt) {
if (bm_opt.value()->has(desired)) {
return bookmark_metadata_context{
*vl_iter, bm_opt.value(), next_line};
}
}
if (vl_iter == bv.begin()) {
return bookmark_metadata_context{
nonstd::nullopt, nonstd::nullopt, next_line};
}
--vl_iter;
}
return bookmark_metadata_context{
nonstd::nullopt, nonstd::nullopt, next_line};
}
nonstd::optional<bookmark_metadata*>
logfile_sub_source::find_bookmark_metadata(content_line_t cl)
logfile_sub_source::find_bookmark_metadata(content_line_t cl) const
{
auto line_pair = this->find_line_with_file(cl).value();
auto line_number = static_cast<uint32_t>(
@ -2806,3 +2913,84 @@ logfile_sub_source::row_for(const row_info& ri)
return nonstd::nullopt;
}
nonstd::optional<vis_line_t>
logfile_sub_source::row_for_anchor(const std::string& id)
{
auto& vb = this->tss_view->get_bookmarks();
const auto& bv = vb[&textview_curses::BM_PARTITION];
for (const auto& vl : bv) {
auto meta_opt = this->find_bookmark_metadata(vl);
if (!meta_opt || meta_opt.value()->bm_name.empty()) {
continue;
}
const auto& name = meta_opt.value()->bm_name;
if (id == text_anchors::to_anchor_string(name)) {
return vl;
}
}
return nonstd::nullopt;
}
nonstd::optional<vis_line_t>
logfile_sub_source::adjacent_anchor(vis_line_t vl, text_anchors::direction dir)
{
auto bmc = this->get_bookmark_metadata_context(
vl, bookmark_metadata::categories::partition);
switch (dir) {
case text_anchors::direction::prev: {
if (bmc.bmc_current && bmc.bmc_current.value() != vl) {
return bmc.bmc_current;
}
if (!bmc.bmc_current || bmc.bmc_current.value() == 0_vl) {
return 0_vl;
}
auto prev_bmc = this->get_bookmark_metadata_context(
bmc.bmc_current.value() - 1_vl,
bookmark_metadata::categories::partition);
if (!prev_bmc.bmc_current) {
return 0_vl;
}
return prev_bmc.bmc_current;
}
case text_anchors::direction::next:
return bmc.bmc_next_line;
}
return nonstd::nullopt;
}
nonstd::optional<std::string>
logfile_sub_source::anchor_for_row(vis_line_t vl)
{
auto line_meta = this->get_bookmark_metadata_context(
vl, bookmark_metadata::categories::partition);
if (!line_meta.bmc_current_metadata) {
return nonstd::nullopt;
}
return text_anchors::to_anchor_string(
line_meta.bmc_current_metadata.value()->bm_name);
}
std::unordered_set<std::string>
logfile_sub_source::get_anchors()
{
auto& vb = this->tss_view->get_bookmarks();
const auto& bv = vb[&textview_curses::BM_PARTITION];
std::unordered_set<std::string> retval;
for (const auto& vl : bv) {
auto meta_opt = this->find_bookmark_metadata(vl);
if (!meta_opt || meta_opt.value()->bm_name.empty()) {
continue;
}
const auto& name = meta_opt.value()->bm_name;
retval.emplace(text_anchors::to_anchor_string(name));
}
return retval;
}

@ -200,7 +200,8 @@ class logfile_sub_source
: public text_sub_source
, public text_time_translator
, public text_accel_source
, public list_input_delegate {
, public list_input_delegate
, public text_anchors {
public:
const static bookmark_type_t BM_ERRORS;
const static bookmark_type_t BM_WARNINGS;
@ -351,10 +352,22 @@ public:
return this->get_bookmark_metadata(this->at(vl));
}
struct bookmark_metadata_context {
nonstd::optional<vis_line_t> bmc_current;
nonstd::optional<bookmark_metadata*> bmc_current_metadata;
nonstd::optional<vis_line_t> bmc_next_line;
};
bookmark_metadata_context get_bookmark_metadata_context(
vis_line_t vl,
bookmark_metadata::categories desired
= bookmark_metadata::categories::any) const;
nonstd::optional<bookmark_metadata*> find_bookmark_metadata(
content_line_t cl);
content_line_t cl) const;
nonstd::optional<bookmark_metadata*> find_bookmark_metadata(vis_line_t vl)
nonstd::optional<bookmark_metadata*> find_bookmark_metadata(
vis_line_t vl) const
{
return this->find_bookmark_metadata(this->at(vl));
}
@ -712,6 +725,14 @@ public:
big_array<indexed_content> lss_index;
nonstd::optional<vis_line_t> row_for_anchor(const std::string& id);
nonstd::optional<vis_line_t> adjacent_anchor(vis_line_t vl, direction dir);
nonstd::optional<std::string> anchor_for_row(vis_line_t vl);
std::unordered_set<std::string> get_anchors();
protected:
void text_accel_display_changed() { this->clear_line_size_cache(); }

@ -482,10 +482,10 @@ add_tag_possibilities()
if (lnav_data.ld_view_stack.top().value_or(nullptr)
== &lnav_data.ld_views[LNV_LOG])
{
logfile_sub_source& lss = lnav_data.ld_log_source;
auto& lss = lnav_data.ld_log_source;
if (lss.text_line_count() > 0) {
auto line_meta_opt = lss.find_bookmark_metadata(
lnav_data.ld_views[LNV_LOG].get_top());
lnav_data.ld_views[LNV_LOG].get_selection());
if (line_meta_opt) {
rc->add_possibility(ln_mode_t::COMMAND,
"line-tags",

@ -604,7 +604,7 @@ load_time_bookmarks()
bool meta = false;
if (part_name != nullptr && part_name[0] != '\0') {
lss.set_user_mark(&textview_curses::BM_META,
lss.set_user_mark(&textview_curses::BM_PARTITION,
line_cl);
bm_meta[line_number].bm_name = part_name;
meta = true;
@ -1041,7 +1041,7 @@ save_user_bookmarks(sqlite3* db,
}
} else {
const auto& line_meta = *(line_meta_opt.value());
if (line_meta.empty()) {
if (line_meta.empty(bookmark_metadata::categories::any)) {
continue;
}
@ -1286,7 +1286,11 @@ save_time_bookmarks()
}
save_user_bookmarks(db.in(), stmt.in(), bm[&textview_curses::BM_USER]);
save_user_bookmarks(db.in(), stmt.in(), bm[&textview_curses::BM_META]);
auto all_meta_marks = bm[&textview_curses::BM_META];
const auto& bm_parts = bm[&textview_curses::BM_PARTITION];
all_meta_marks.insert(
all_meta_marks.end(), bm_parts.begin(), bm_parts.end());
save_user_bookmarks(db.in(), stmt.in(), all_meta_marks);
if (sqlite3_prepare_v2(db.in(),
"DELETE FROM time_offset WHERE "

@ -180,6 +180,7 @@ const bookmark_type_t textview_curses::BM_USER("user");
const bookmark_type_t textview_curses::BM_USER_EXPR("user-expr");
const bookmark_type_t textview_curses::BM_SEARCH("search");
const bookmark_type_t textview_curses::BM_META("meta");
const bookmark_type_t textview_curses::BM_PARTITION("partition");
textview_curses::textview_curses()
: lnav_config_listener(__FILE__), tc_search_action(noop_func{})
@ -555,8 +556,9 @@ textview_curses::textview_value_for_row(vis_line_t row, attr_line_t& value_out)
const auto& user_marks = this->tc_bookmarks[&BM_USER];
const auto& user_expr_marks = this->tc_bookmarks[&BM_USER_EXPR];
if (binary_search(user_marks.begin(), user_marks.end(), row)
|| binary_search(user_expr_marks.begin(), user_expr_marks.end(), row))
if (std::binary_search(user_marks.begin(), user_marks.end(), row)
|| std::binary_search(
user_expr_marks.begin(), user_expr_marks.end(), row))
{
sa.emplace_back(line_range{orig_line.lr_start, -1},
VC_STYLE.value(text_attrs{A_REVERSE}));

@ -576,6 +576,7 @@ public:
const static bookmark_type_t BM_USER_EXPR;
const static bookmark_type_t BM_SEARCH;
const static bookmark_type_t BM_META;
const static bookmark_type_t BM_PARTITION;
textview_curses();

@ -160,7 +160,7 @@ view_curses::mvwattrline(WINDOW* window,
line_range lr_bytes;
int char_index = 0;
for (size_t lpc = 0; lpc < line.size(); lpc++) {
for (size_t lpc = 0; lpc < line.size();) {
int exp_start_index = expanded_line.size();
auto ch = static_cast<unsigned char>(line[lpc]);
@ -178,6 +178,7 @@ view_curses::mvwattrline(WINDOW* window,
} while (expanded_line.size() % 8);
utf_adjustments.emplace_back(
lpc, expanded_line.size() - exp_start_index - 1);
lpc += 1;
break;
}
@ -185,49 +186,48 @@ view_curses::mvwattrline(WINDOW* window,
expanded_line.append("\u238b");
utf_adjustments.emplace_back(lpc, -1);
char_index += 1;
lpc += 1;
break;
case '\b':
expanded_line.append("\u232b");
utf_adjustments.emplace_back(lpc, -1);
char_index += 1;
lpc += 1;
break;
case '\r':
case '\n':
expanded_line.push_back(' ');
char_index += 1;
lpc += 1;
break;
default: {
auto size_result = ww898::utf::utf8::char_size([&line, lpc]() {
return std::make_pair(line[lpc], line.length() - lpc - 1);
});
auto exp_read_start = expanded_line.size();
auto lpc_start = lpc;
auto read_res
= ww898::utf::utf8::read([&line, &expanded_line, &lpc]() {
auto ch = line[lpc++];
expanded_line.push_back(ch);
return ch;
});
if (size_result.isErr()) {
if (read_res.isErr()) {
log_trace(
"error:%d:%d:%s", y, x + lpc, read_res.unwrapErr());
expanded_line.resize(exp_read_start);
expanded_line.push_back('?');
char_index += 1;
lpc = lpc_start + 1;
} else {
auto offset = 1 - (int) size_result.unwrap();
expanded_line.push_back(ch);
if (offset) {
#if 0
if (char_index < lr_chars.lr_start) {
lr_bytes.lr_start += abs(offset);
}
if (char_index < lr_chars.lr_end) {
lr_bytes.lr_end += abs(offset);
}
#endif
utf_adjustments.emplace_back(lpc, offset);
for (; offset && (lpc + 1) < line.size();
lpc++, offset++)
{
expanded_line.push_back(line[lpc + 1]);
}
if (lpc > (lpc_start + 1)) {
utf_adjustments.emplace_back(lpc_start,
1 - (lpc - lpc_start));
}
auto wch = read_res.unwrap();
char_index += wcwidth(wch);
}
char_index += 1;
break;
}
}

@ -89,8 +89,7 @@ struct utf8 final {
if (ch0 < 0x80) // 0xxx_xxxx
return Ok((uint32_t) ch0);
if (ch0 < 0xC0)
throw std::runtime_error(
"The utf8 first char in sequence is incorrect");
return Err("The utf8 first char in sequence is incorrect");
if (ch0 < 0xE0) // 110x_xxxx 10xx_xxxx
{
char_type const ch1 = read_fn();

@ -343,6 +343,7 @@ dist_noinst_DATA = \
logfile_nested_json.json \
logfile_nextcloud.0 \
logfile_openam.0 \
logfile_partitions.0 \
logfile_plain.0 \
logfile_pretty.0 \
logfile_procstate.0 \
@ -412,6 +413,7 @@ dist_noinst_DATA = \
formats/jsontest3/format.json \
formats/jsontest-subsec/format.json \
formats/nestedjson/format.json \
formats/partitions/format.json \
formats/scripts/multiline-echo.lnav \
formats/scripts/redirecting.lnav \
formats/scripts/nested-redirecting.lnav \

@ -530,6 +530,8 @@ EXPECTED_FILES = \
$(srcdir)/%reldir%/test_sessions.sh_430b9522ba1a37983138f3c4935cba91b781e415.out \
$(srcdir)/%reldir%/test_sessions.sh_4f13dd3858546b6e04a27e244159d355e368f2ae.err \
$(srcdir)/%reldir%/test_sessions.sh_4f13dd3858546b6e04a27e244159d355e368f2ae.out \
$(srcdir)/%reldir%/test_sessions.sh_639b83ce8f67975dfdc7086946ec287b43b6fa8c.err \
$(srcdir)/%reldir%/test_sessions.sh_639b83ce8f67975dfdc7086946ec287b43b6fa8c.out \
$(srcdir)/%reldir%/test_sessions.sh_68a89b56c5e7f7db620084cca1eb547cbb19a2c9.err \
$(srcdir)/%reldir%/test_sessions.sh_68a89b56c5e7f7db620084cca1eb547cbb19a2c9.out \
$(srcdir)/%reldir%/test_sessions.sh_6d87ff483d5785c58fb271a405ff1c35e4f83cd9.err \
@ -610,6 +612,8 @@ EXPECTED_FILES = \
$(srcdir)/%reldir%/test_sql.sh_2f15b8a38673ac4db45dc6ed2eafe609c332575b.out \
$(srcdir)/%reldir%/test_sql.sh_31df37f254255115611fc321b63374a2fa4a1cd5.err \
$(srcdir)/%reldir%/test_sql.sh_31df37f254255115611fc321b63374a2fa4a1cd5.out \
$(srcdir)/%reldir%/test_sql.sh_3445b783808f174b76f55dc6b998f721a1aae271.err \
$(srcdir)/%reldir%/test_sql.sh_3445b783808f174b76f55dc6b998f721a1aae271.out \
$(srcdir)/%reldir%/test_sql.sh_3d77a2092192caf98e141a6039e886ede836f044.err \
$(srcdir)/%reldir%/test_sql.sh_3d77a2092192caf98e141a6039e886ede836f044.out \
$(srcdir)/%reldir%/test_sql.sh_4090f96ea11a344c1e2939211da778992dab47d8.err \

@ -5081,10 +5081,10 @@
"keymap_def_db_view": "Press ${ansi_bold}v${ansi_norm}/${ansi_bold}V${ansi_norm} to switch to the SQL result view",
"keymap_def_hist_view": "Press ${ansi_bold}i${ansi_norm}/${ansi_bold}I${ansi_norm} to switch to the histogram view",
"keymap_def_next_mark": "Press ${ansi_bold}c${ansi_norm} to copy marked lines to the clipboard; press ${ansi_bold}C${ansi_norm} to clear marked lines",
"keymap_def_next_section": "Press ${ansi_bold}}${ansi_norm} to move to the next section in history",
"keymap_def_next_section": "Press ${ansi_bold}}${ansi_norm} to move to the next section in the view",
"keymap_def_next_user_mark": "Press ${ansi_bold}u${ansi_norm}/${ansi_bold}U${ansi_norm} to move forward/backward through user bookmarks",
"keymap_def_pop_view": "Press ${ansi_bold}q${ansi_norm} to return to the previous view",
"keymap_def_prev_section": "Press ${ansi_bold}{${ansi_norm} to move to the previous section in history",
"keymap_def_prev_section": "Press ${ansi_bold}{${ansi_norm} to move to the previous section in the view",
"keymap_def_scroll_horiz": "Press \\'${ansi_bold}>${ansi_norm}\\' or \\'${ansi_bold}<${ansi_norm}\\' to scroll horizontally to a search result",
"keymap_def_text_view": "Press ${ansi_bold}t${ansi_norm} to switch to the text view",
"keymap_def_time_offset": "Press ${ansi_bold}s${ansi_norm}/${ansi_bold}S${ansi_norm} to move forward/backward through slow downs",

@ -1,4 +1,4 @@
✘ error: unknown bookmark type: foobar
 --> command-option:2
 | :next-mark foobar 
 = help: available types: error, file, meta, search, user, user-expr, warning
 = help: available types: error, file, meta, partition, search, user, user-expr, warning

@ -260,11 +260,10 @@ can always use  q  to pop the top view off of the stack.
and then the last message arrives five seconds
later, the last message will be highlighted as a
slow down.
{/} Move to the previous/next location in history.
Whenever you jump to a new location in the view,
the location will be added to the history. The
history is not updated when using only the arrow
keys.
{/} Move to the previous/next section in the view. In
the LOG view, this moves through partitions. In
other views, it moves through sections of
documents.
Chronological Navigation

@ -0,0 +1,89 @@
[
{
"top_meta": {
"time": "2009-07-20 22:59:29.000",
"file": "{test_dir}/logfile_access_log.0",
"anchor": "#middle",
"breadcrumbs": [
{
"display_value": "⊑ middle",
"search_placeholder": "",
"possibilities": [
{
"display_value": "middle"
}
]
},
{
"display_value": "2009-07-20T22:59:29.000",
"search_placeholder": "(Enter an absolute or relative time)",
"possibilities": [
{
"display_value": "-1 day"
},
{
"display_value": "-1h"
},
{
"display_value": "-30m"
},
{
"display_value": "-15m"
},
{
"display_value": "-5m"
},
{
"display_value": "-1m"
},
{
"display_value": "+1m"
},
{
"display_value": "+5m"
},
{
"display_value": "+15m"
},
{
"display_value": "+30m"
},
{
"display_value": "+1h"
},
{
"display_value": "+1 day"
}
]
},
{
"display_value": "access_log",
"search_placeholder": "",
"possibilities": [
{
"display_value": "access_log"
}
]
},
{
"display_value": "logfile_access_log.0[2]",
"search_placeholder": "",
"possibilities": [
{
"display_value": "logfile_access_log.0"
}
]
},
{
"display_value": "192.168.202.254",
"search_placeholder": "",
"possibilities": [
{
"display_value": "192.168.202.254"
}
]
}
]
}
}
]

@ -0,0 +1,83 @@
log_line,log_part,log_body
0,<NULL>,restart.
1,<NULL>,Invalid query packet.
2,<NULL>,DHCPREQUEST on eth0 to 10.1.10.1 port 67 (xid=0x654a04aa)
3,<NULL>,DHCPNAK from 10.1.10.1 (xid=0x654a04aa)
4,<NULL>,/sbin/dhclient-script : updated /etc/resolv.conf
5,<NULL>,Withdrawing address record for 10.1.10.49 on eth0.
6,<NULL>,Leaving mDNS multicast group on interface eth0.IPv4 with address 10.1.10.49.
7,<NULL>,iface.c: interface_mdns_mcast_join() called but no local address available.
8,<NULL>,Interface eth0.IPv4 no longer relevant for mDNS.
9,<NULL>,DHCPDISCOVER on eth0 to 255.255.255.255 port 67 interval 5 (xid=0x4e17f141)
10,<NULL>,DHCPOFFER from 10.1.10.1
11,<NULL>,DHCPREQUEST on eth0 to 255.255.255.255 port 67 (xid=0x4e17f141)
12,<NULL>,DHCPACK from 10.1.10.1 (xid=0x4e17f141)
13,<NULL>,New relevant interface eth0.IPv4 for mDNS.
14,<NULL>,Joining mDNS multicast group on interface eth0.IPv4 with address 10.1.10.103.
15,<NULL>,Registering new address record for 10.1.10.103 on eth0.
16,<NULL>,Withdrawing address record for 10.1.10.103 on eth0.
17,<NULL>,Leaving mDNS multicast group on interface eth0.IPv4 with address 10.1.10.103.
18,<NULL>,iface.c: interface_mdns_mcast_join() called but no local address available.
19,<NULL>,Interface eth0.IPv4 no longer relevant for mDNS.
20,<NULL>,New relevant interface eth0.IPv4 for mDNS.
21,<NULL>,Joining mDNS multicast group on interface eth0.IPv4 with address 10.1.10.103.
22,<NULL>,Registering new address record for 10.1.10.103 on eth0.
23,<NULL>,/sbin/dhclient-script : updated /etc/resolv.conf
24,10.1.10.103,bound to 10.1.10.103 -- renewal in 54694 seconds.
25,10.1.10.103,Invalid query packet.
26,10.1.10.103,Invalid query packet.
27,10.1.10.103,DHCPREQUEST on eth0 to 10.1.10.1 port 67 (xid=0x4e17f141)
28,10.1.10.103,DHCPACK from 10.1.10.1 (xid=0x4e17f141)
29,10.1.10.103,bound to 10.1.10.103 -- renewal in 8787 seconds.
30,10.1.10.103,Invalid query packet.
31,10.1.10.103,DHCPREQUEST on eth0 to 10.1.10.1 port 67 (xid=0x4e17f141)
32,10.1.10.103,DHCPACK from 10.1.10.1 (xid=0x4e17f141)
33,10.1.10.103,bound to 10.1.10.103 -- renewal in 9938 seconds.
34,10.1.10.103,Invalid query packet.
35,10.1.10.103,DHCPREQUEST on eth0 to 10.1.10.1 port 67 (xid=0x4e17f141)
36,10.1.10.103,DHCPACK from 10.1.10.1 (xid=0x4e17f141)
37,10.1.10.103,bound to 10.1.10.103 -- renewal in 2656 seconds.
38,10.1.10.103,Invalid query packet.
39,10.1.10.103,DHCPREQUEST on eth0 to 10.1.10.1 port 67 (xid=0x4e17f141)
40,10.1.10.103,DHCPACK from 10.1.10.1 (xid=0x4e17f141)
41,10.1.10.103,bound to 10.1.10.103 -- renewal in 15112 seconds.
42,10.1.10.103,Invalid query packet.
43,10.1.10.103,Invalid query packet.
44,10.1.10.103,Invalid query packet.
45,10.1.10.103,DHCPREQUEST on eth0 to 10.1.10.1 port 67 (xid=0x4e17f141)
46,10.1.10.103,DHCPNAK from 10.1.10.1 (xid=0x4e17f141)
47,10.1.10.103,/sbin/dhclient-script : updated /etc/resolv.conf
48,10.1.10.103,Withdrawing address record for 10.1.10.103 on eth0.
49,10.1.10.103,Leaving mDNS multicast group on interface eth0.IPv4 with address 10.1.10.103.
50,10.1.10.103,iface.c: interface_mdns_mcast_join() called but no local address available.
51,10.1.10.103,Interface eth0.IPv4 no longer relevant for mDNS.
52,10.1.10.103,DHCPDISCOVER on eth0 to 255.255.255.255 port 67 interval 5 (xid=0xd16b79d)
53,10.1.10.103,DHCPOFFER from 10.1.10.1
54,10.1.10.103,DHCPREQUEST on eth0 to 255.255.255.255 port 67 (xid=0xd16b79d)
55,10.1.10.103,DHCPACK from 10.1.10.1 (xid=0xd16b79d)
56,10.1.10.103,New relevant interface eth0.IPv4 for mDNS.
57,10.1.10.103,Joining mDNS multicast group on interface eth0.IPv4 with address 10.1.10.62.
58,10.1.10.103,Registering new address record for 10.1.10.62 on eth0.
59,10.1.10.103,Withdrawing address record for 10.1.10.62 on eth0.
60,10.1.10.103,Leaving mDNS multicast group on interface eth0.IPv4 with address 10.1.10.62.
61,10.1.10.103,iface.c: interface_mdns_mcast_join() called but no local address available.
62,10.1.10.103,Interface eth0.IPv4 no longer relevant for mDNS.
63,10.1.10.103,New relevant interface eth0.IPv4 for mDNS.
64,10.1.10.103,Joining mDNS multicast group on interface eth0.IPv4 with address 10.1.10.62.
65,10.1.10.103,Registering new address record for 10.1.10.62 on eth0.
66,10.1.10.103,/sbin/dhclient-script : updated /etc/resolv.conf
67,10.1.10.62,bound to 10.1.10.62 -- renewal in 31782 seconds.
68,10.1.10.62,Invalid query packet.
69,10.1.10.62,Invalid query packet.
70,10.1.10.62,DHCPREQUEST on eth0 to 10.1.10.1 port 67 (xid=0xd16b79d)
71,10.1.10.62,DHCPACK from 10.1.10.1 (xid=0xd16b79d)
72,10.1.10.62,bound to 10.1.10.62 -- renewal in 19742 seconds.
73,10.1.10.62,Invalid query packet.
74,10.1.10.62,Invalid query packet.
75,10.1.10.62,DHCPREQUEST on eth0 to 10.1.10.1 port 67 (xid=0xd16b79d)
76,10.1.10.62,DHCPACK from 10.1.10.1 (xid=0xd16b79d)
77,10.1.10.62,bound to 10.1.10.62 -- renewal in 55327 seconds.
78,10.1.10.62,Invalid query packet.
79,10.1.10.62,Invalid response packet from host 10.1.10.10.
80,10.1.10.62,Invalid response packet from host fe80::22c9:d0ff:fe15:1b7c.
81,10.1.10.62,Invalid query packet.

@ -0,0 +1,11 @@
{
"$schema": "https://lnav.org/schemas/format-v1.schema.json",
"syslog_log": {
"partitions": {
"dhclient-binding": {
"description": "Set partition name to current IP",
"pattern": " bound to ([^ ]+) --"
}
}
}
}

@ -0,0 +1,82 @@
Apr 28 04:02:03 tstack-centos5 syslogd 1.4.1: restart.
Apr 28 04:04:01 tstack-centos5 avahi-daemon[2467]: Invalid query packet.
Apr 28 06:53:54 tstack-centos5 dhclient: DHCPREQUEST on eth0 to 10.1.10.1 port 67 (xid=0x654a04aa)
Apr 28 06:53:54 tstack-centos5 dhclient: DHCPNAK from 10.1.10.1 (xid=0x654a04aa)
Apr 28 06:53:54 tstack-centos5 NET[31050]: /sbin/dhclient-script : updated /etc/resolv.conf
Apr 28 06:53:54 tstack-centos5 avahi-daemon[2467]: Withdrawing address record for 10.1.10.49 on eth0.
Apr 28 06:53:54 tstack-centos5 avahi-daemon[2467]: Leaving mDNS multicast group on interface eth0.IPv4 with address 10.1.10.49.
Apr 28 06:53:54 tstack-centos5 avahi-daemon[2467]: iface.c: interface_mdns_mcast_join() called but no local address available.
Apr 28 06:53:54 tstack-centos5 avahi-daemon[2467]: Interface eth0.IPv4 no longer relevant for mDNS.
Apr 28 06:53:54 tstack-centos5 dhclient: DHCPDISCOVER on eth0 to 255.255.255.255 port 67 interval 5 (xid=0x4e17f141)
Apr 28 06:53:55 tstack-centos5 dhclient: DHCPOFFER from 10.1.10.1
Apr 28 06:53:55 tstack-centos5 dhclient: DHCPREQUEST on eth0 to 255.255.255.255 port 67 (xid=0x4e17f141)
Apr 28 06:53:55 tstack-centos5 dhclient: DHCPACK from 10.1.10.1 (xid=0x4e17f141)
Apr 28 06:53:55 tstack-centos5 avahi-daemon[2467]: New relevant interface eth0.IPv4 for mDNS.
Apr 28 06:53:55 tstack-centos5 avahi-daemon[2467]: Joining mDNS multicast group on interface eth0.IPv4 with address 10.1.10.103.
Apr 28 06:53:55 tstack-centos5 avahi-daemon[2467]: Registering new address record for 10.1.10.103 on eth0.
Apr 28 06:53:55 tstack-centos5 avahi-daemon[2467]: Withdrawing address record for 10.1.10.103 on eth0.
Apr 28 06:53:55 tstack-centos5 avahi-daemon[2467]: Leaving mDNS multicast group on interface eth0.IPv4 with address 10.1.10.103.
Apr 28 06:53:55 tstack-centos5 avahi-daemon[2467]: iface.c: interface_mdns_mcast_join() called but no local address available.
Apr 28 06:53:55 tstack-centos5 avahi-daemon[2467]: Interface eth0.IPv4 no longer relevant for mDNS.
Apr 28 06:53:55 tstack-centos5 avahi-daemon[2467]: New relevant interface eth0.IPv4 for mDNS.
Apr 28 06:53:55 tstack-centos5 avahi-daemon[2467]: Joining mDNS multicast group on interface eth0.IPv4 with address 10.1.10.103.
Apr 28 06:53:55 tstack-centos5 avahi-daemon[2467]: Registering new address record for 10.1.10.103 on eth0.
Apr 28 06:53:55 tstack-centos5 NET[31132]: /sbin/dhclient-script : updated /etc/resolv.conf
Apr 28 06:53:55 tstack-centos5 dhclient: bound to 10.1.10.103 -- renewal in 54694 seconds.
Apr 28 07:03:50 tstack-centos5 avahi-daemon[2467]: Invalid query packet.
Apr 28 17:01:19 tstack-centos5 avahi-daemon[2467]: Invalid query packet.
Apr 28 22:05:28 tstack-centos5 dhclient: DHCPREQUEST on eth0 to 10.1.10.1 port 67 (xid=0x4e17f141)
Apr 28 22:05:29 tstack-centos5 dhclient: DHCPACK from 10.1.10.1 (xid=0x4e17f141)
Apr 28 22:05:29 tstack-centos5 dhclient: bound to 10.1.10.103 -- renewal in 8787 seconds.
Apr 28 22:10:01 tstack-centos5 avahi-daemon[2467]: Invalid query packet.
Apr 29 00:31:55 tstack-centos5 dhclient: DHCPREQUEST on eth0 to 10.1.10.1 port 67 (xid=0x4e17f141)
Apr 29 00:31:56 tstack-centos5 dhclient: DHCPACK from 10.1.10.1 (xid=0x4e17f141)
Apr 29 00:31:56 tstack-centos5 dhclient: bound to 10.1.10.103 -- renewal in 9938 seconds.
Apr 29 00:39:19 tstack-centos5 avahi-daemon[2467]: Invalid query packet.
Apr 29 03:17:34 tstack-centos5 dhclient: DHCPREQUEST on eth0 to 10.1.10.1 port 67 (xid=0x4e17f141)
Apr 29 03:17:34 tstack-centos5 dhclient: DHCPACK from 10.1.10.1 (xid=0x4e17f141)
Apr 29 03:17:34 tstack-centos5 dhclient: bound to 10.1.10.103 -- renewal in 2656 seconds.
Apr 29 03:18:07 tstack-centos5 avahi-daemon[2467]: Invalid query packet.
Apr 29 04:01:50 tstack-centos5 dhclient: DHCPREQUEST on eth0 to 10.1.10.1 port 67 (xid=0x4e17f141)
Apr 29 04:01:50 tstack-centos5 dhclient: DHCPACK from 10.1.10.1 (xid=0x4e17f141)
Apr 29 04:01:50 tstack-centos5 dhclient: bound to 10.1.10.103 -- renewal in 15112 seconds.
Apr 29 04:10:03 tstack-centos5 avahi-daemon[2467]: Invalid query packet.
Apr 29 05:55:18 tstack-centos5 avahi-daemon[2467]: Invalid query packet.
Apr 29 06:39:04 tstack-centos5 avahi-daemon[2467]: Invalid query packet.
Apr 29 08:13:41 tstack-centos5 dhclient: DHCPREQUEST on eth0 to 10.1.10.1 port 67 (xid=0x4e17f141)
Apr 29 08:13:42 tstack-centos5 dhclient: DHCPNAK from 10.1.10.1 (xid=0x4e17f141)
Apr 29 08:13:42 tstack-centos5 NET[13600]: /sbin/dhclient-script : updated /etc/resolv.conf
Apr 29 08:13:42 tstack-centos5 avahi-daemon[2467]: Withdrawing address record for 10.1.10.103 on eth0.
Apr 29 08:13:42 tstack-centos5 avahi-daemon[2467]: Leaving mDNS multicast group on interface eth0.IPv4 with address 10.1.10.103.
Apr 29 08:13:42 tstack-centos5 avahi-daemon[2467]: iface.c: interface_mdns_mcast_join() called but no local address available.
Apr 29 08:13:42 tstack-centos5 avahi-daemon[2467]: Interface eth0.IPv4 no longer relevant for mDNS.
Apr 29 08:13:42 tstack-centos5 dhclient: DHCPDISCOVER on eth0 to 255.255.255.255 port 67 interval 5 (xid=0xd16b79d)
Apr 29 08:13:43 tstack-centos5 dhclient: DHCPOFFER from 10.1.10.1
Apr 29 08:13:43 tstack-centos5 dhclient: DHCPREQUEST on eth0 to 255.255.255.255 port 67 (xid=0xd16b79d)
Apr 29 08:13:43 tstack-centos5 dhclient: DHCPACK from 10.1.10.1 (xid=0xd16b79d)
Apr 29 08:13:43 tstack-centos5 avahi-daemon[2467]: New relevant interface eth0.IPv4 for mDNS.
Apr 29 08:13:43 tstack-centos5 avahi-daemon[2467]: Joining mDNS multicast group on interface eth0.IPv4 with address 10.1.10.62.
Apr 29 08:13:43 tstack-centos5 avahi-daemon[2467]: Registering new address record for 10.1.10.62 on eth0.
Apr 29 08:13:43 tstack-centos5 avahi-daemon[2467]: Withdrawing address record for 10.1.10.62 on eth0.
Apr 29 08:13:43 tstack-centos5 avahi-daemon[2467]: Leaving mDNS multicast group on interface eth0.IPv4 with address 10.1.10.62.
Apr 29 08:13:43 tstack-centos5 avahi-daemon[2467]: iface.c: interface_mdns_mcast_join() called but no local address available.
Apr 29 08:13:43 tstack-centos5 avahi-daemon[2467]: Interface eth0.IPv4 no longer relevant for mDNS.
Apr 29 08:13:43 tstack-centos5 avahi-daemon[2467]: New relevant interface eth0.IPv4 for mDNS.
Apr 29 08:13:43 tstack-centos5 avahi-daemon[2467]: Joining mDNS multicast group on interface eth0.IPv4 with address 10.1.10.62.
Apr 29 08:13:43 tstack-centos5 avahi-daemon[2467]: Registering new address record for 10.1.10.62 on eth0.
Apr 29 08:13:43 tstack-centos5 NET[13682]: /sbin/dhclient-script : updated /etc/resolv.conf
Apr 29 08:13:43 tstack-centos5 dhclient: bound to 10.1.10.62 -- renewal in 31782 seconds.
Apr 29 08:15:06 tstack-centos5 avahi-daemon[2467]: Invalid query packet.
Apr 29 17:03:19 tstack-centos5 avahi-daemon[2467]: Invalid query packet.
Apr 29 17:03:25 tstack-centos5 dhclient: DHCPREQUEST on eth0 to 10.1.10.1 port 67 (xid=0xd16b79d)
Apr 29 17:03:25 tstack-centos5 dhclient: DHCPACK from 10.1.10.1 (xid=0xd16b79d)
Apr 29 17:03:25 tstack-centos5 dhclient: bound to 10.1.10.62 -- renewal in 19742 seconds.
Apr 29 17:03:51 tstack-centos5 avahi-daemon[2467]: Invalid query packet.
Apr 29 17:20:00 tstack-centos5 avahi-daemon[2467]: Invalid query packet.
Apr 29 22:32:26 tstack-centos5 dhclient: DHCPREQUEST on eth0 to 10.1.10.1 port 67 (xid=0xd16b79d)
Apr 29 22:32:27 tstack-centos5 dhclient: DHCPACK from 10.1.10.1 (xid=0xd16b79d)
Apr 29 22:32:27 tstack-centos5 dhclient: bound to 10.1.10.62 -- renewal in 55327 seconds.
Apr 29 22:45:57 tstack-centos5 avahi-daemon[2467]: Invalid query packet.
Apr 29 23:02:45 tstack-centos5 avahi-daemon[2467]: Invalid response packet from host 10.1.10.10.
Apr 29 23:02:45 tstack-centos5 avahi-daemon[2467]: Invalid response packet from host fe80::22c9:d0ff:fe15:1b7c.
Apr 29 23:02:46 tstack-centos5 avahi-daemon[2467]: Invalid query packet.

@ -78,6 +78,14 @@ run_cap_test ${lnav_test} -n \
-c ':write-csv-to -' \
${test_dir}/logfile_access_log.0
# partition name was not saved in session and doesn't not show up in crumbs
run_cap_test ${lnav_test} -n \
-c ":load-session" \
-c ':goto 2' \
-c ";SELECT top_meta FROM lnav_views WHERE name = 'log'" \
-c ':write-json-to -' \
${test_dir}/logfile_access_log.0
# adjust time is not working
run_cap_test ${lnav_test} -nq \
-c ":adjust-log-time 2010-01-01T00:00:00" \

@ -822,6 +822,12 @@ log_line,log_part
2,<NULL>
EOF
# test the partitions defined in the format
run_cap_test ${lnav_test} -n \
-I ${test_dir} \
-c ";SELECT log_line, log_part, log_body FROM syslog_log" \
-c ":write-csv-to -" \
${test_dir}/logfile_partitions.0
run_test ${lnav_test} -n \
-c ";SELECT * FROM openam_log" \

Loading…
Cancel
Save